Skip to content

Commit

Permalink
Allow multiple conditions with AND operator in color or icon rules
Browse files Browse the repository at this point in the history
Closes #3058

Signed-off-by: Laurent Garnier <[email protected]>
  • Loading branch information
lolodomo committed Sep 27, 2023
1 parent 4379d71 commit fb0ab17
Show file tree
Hide file tree
Showing 7 changed files with 253 additions and 176 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,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.IconArray;
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 Down Expand Up @@ -122,24 +123,40 @@ private Set<Item> getAllItems(EList<Widget> widgets) {
items.addAll(getAllItems(frame.getChildren()));
}
// now scan dynamic icon rules
for (IconArray rule : widget.getDynamicIcon()) {
addItemWithName(items, rule.getItem());
for (IconRule rule : widget.getDynamicIcon()) {
if (rule.getConditions() != null) {
for (Condition condition : rule.getConditions()) {
addItemWithName(items, condition.getItem());
}
}
}
// now scan visibility rules
for (VisibilityRule rule : widget.getVisibility()) {
addItemWithName(items, rule.getItem());
}
// now scan label color rules
for (ColorArray rule : widget.getLabelColor()) {
addItemWithName(items, rule.getItem());
if (rule.getConditions() != null) {
for (Condition condition : rule.getConditions()) {
addItemWithName(items, condition.getItem());
}
}
}
// now scan value color rules
for (ColorArray rule : widget.getValueColor()) {
addItemWithName(items, rule.getItem());
if (rule.getConditions() != null) {
for (Condition condition : rule.getConditions()) {
addItemWithName(items, condition.getItem());
}
}
}
// now scan icon color rules
for (ColorArray rule : widget.getIconColor()) {
addItemWithName(items, rule.getItem());
if (rule.getConditions() != null) {
for (Condition condition : rule.getConditions()) {
addItemWithName(items, condition.getItem());
}
}
}
}
}
Expand Down Expand Up @@ -248,10 +265,20 @@ private Item getItemForWidget(Widget w) {

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.getDynamicIcon().stream().anyMatch(r -> name.equals(r.getItem()));
|| w.getLabelColor().stream().anyMatch(r -> colorRuleDependsOnItem(r, name))
|| w.getValueColor().stream().anyMatch(r -> colorRuleDependsOnItem(r, name))
|| w.getIconColor().stream().anyMatch(r -> colorRuleDependsOnItem(r, name))
|| w.getDynamicIcon().stream().anyMatch(r -> iconRuleDependsOnItem(r, name));
}

private boolean colorRuleDependsOnItem(ColorArray rule, String name) {
return rule != null && rule.getConditions() != null
&& rule.getConditions().stream().anyMatch(c -> name.equals(c.getItem()));
}

private boolean iconRuleDependsOnItem(IconRule rule, String name) {
return rule != null && rule.getConditions() != null
&& rule.getConditions().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,8 +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.IconArray;
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 @@ -774,34 +775,42 @@ private Set<GenericItem> getItemsInVisibilityCond(EList<VisibilityRule> ruleList

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) {
if (rule.getConditions() != null) {
for (Condition condition : rule.getConditions()) {
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;
}

private Set<GenericItem> getItemsInIconCond(EList<IconArray> ruleList) {
private Set<GenericItem> getItemsInIconCond(EList<IconRule> ruleList) {
Set<GenericItem> items = new HashSet<>();
for (IconArray rule : ruleList) {
String itemName = rule.getItem();
if (itemName != null) {
try {
Item item = itemUIRegistry.getItem(itemName);
if (item instanceof GenericItem genericItem) {
items.add(genericItem);
for (IconRule rule : ruleList) {
if (rule.getConditions() != null) {
for (Condition condition : rule.getConditions()) {
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
}
}
}
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 @@ -342,21 +343,33 @@ private EList<Widget> initSitemapWidgets() {

// add label color conditions to the item:
ColorArray labelColor = mock(ColorArray.class);
when(labelColor.getItem()).thenReturn(LABEL_COLOR_ITEM_NAME);
Condition conditon = mock(Condition.class);
when(conditon.getItem()).thenReturn(LABEL_COLOR_ITEM_NAME);
BasicEList<Condition> conditions = new BasicEList<>();
conditions.add(conditon);
when(labelColor.getConditions()).thenReturn(conditions);
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 Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ LinkableWidget:
Frame:
{Frame} 'Frame' (('item=' item=ItemRef)? & ('label=' label=(ID | STRING))? &
(('icon=' icon=Icon) |
('icon=[' (dynamicIcon+=IconArray (',' dynamicIcon+=IconArray)* ']')) |
('icon=[' (dynamicIcon+=IconRule (',' dynamicIcon+=IconRule)*) ']') |
('staticIcon=' staticIcon=Icon))? &
('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)* ']'))? &
('valuecolor=[' (ValueColor+=ColorArray (',' ValueColor+=ColorArray)* ']'))? &
Expand All @@ -36,7 +36,7 @@ Frame:
Text:
{Text} 'Text' (('item=' item=ItemRef)? & ('label=' label=(ID | STRING))? &
(('icon=' icon=Icon) |
('icon=[' (dynamicIcon+=IconArray (',' dynamicIcon+=IconArray)* ']')) |
('icon=[' (dynamicIcon+=IconRule (',' dynamicIcon+=IconRule)*) ']') |
('staticIcon=' staticIcon=Icon))? &
('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)* ']'))? &
('valuecolor=[' (ValueColor+=ColorArray (',' ValueColor+=ColorArray)* ']'))? &
Expand All @@ -46,7 +46,7 @@ Text:
Group:
'Group' (('item=' item=GroupItemRef) & ('label=' label=(ID | STRING))? &
(('icon=' icon=Icon) |
('icon=[' (dynamicIcon+=IconArray (',' dynamicIcon+=IconArray)* ']')) |
('icon=[' (dynamicIcon+=IconRule (',' dynamicIcon+=IconRule)*) ']') |
('staticIcon=' staticIcon=Icon))? &
('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)* ']'))? &
('valuecolor=[' (ValueColor+=ColorArray (',' ValueColor+=ColorArray)* ']'))? &
Expand All @@ -56,7 +56,7 @@ Group:
Image:
'Image' (('item=' item=ItemRef)? & ('label=' label=(ID | STRING))? &
(('icon=' icon=Icon) |
('icon=[' (dynamicIcon+=IconArray (',' dynamicIcon+=IconArray)* ']')) |
('icon=[' (dynamicIcon+=IconRule (',' dynamicIcon+=IconRule)*) ']') |
('staticIcon=' staticIcon=Icon))? &
('url=' url=STRING)? & ('refresh=' refresh=INT)? &
('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)* ']'))? &
Expand All @@ -67,7 +67,7 @@ Image:
Video:
'Video' (('item=' item=ItemRef)? & ('label=' label=(ID | STRING))? &
(('icon=' icon=Icon) |
('icon=[' (dynamicIcon+=IconArray (',' dynamicIcon+=IconArray)* ']')) |
('icon=[' (dynamicIcon+=IconRule (',' dynamicIcon+=IconRule)*) ']') |
('staticIcon=' staticIcon=Icon))? &
('url=' url=STRING) & ('encoding=' encoding=STRING)? &
('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)* ']'))? &
Expand All @@ -78,7 +78,7 @@ Video:
Chart:
'Chart' (('item=' item=ItemRef) & ('label=' label=(ID | STRING))? &
(('icon=' icon=Icon) |
('icon=[' (dynamicIcon+=IconArray (',' dynamicIcon+=IconArray)* ']')) |
('icon=[' (dynamicIcon+=IconRule (',' dynamicIcon+=IconRule)*) ']') |
('staticIcon=' staticIcon=Icon))? &
('service=' service=STRING)? & ('refresh=' refresh=INT)? & ('period=' period=ID) &
('legend=' legend=BOOLEAN_OBJECT)? & ('forceasitem=' forceAsItem=BOOLEAN_OBJECT)? &
Expand All @@ -91,7 +91,7 @@ Chart:
Webview:
'Webview' (('item=' item=ItemRef)? & ('label=' label=(ID | STRING))? &
(('icon=' icon=Icon) |
('icon=[' (dynamicIcon+=IconArray (',' dynamicIcon+=IconArray)* ']')) |
('icon=[' (dynamicIcon+=IconRule (',' dynamicIcon+=IconRule)*) ']') |
('staticIcon=' staticIcon=Icon))? &
('height=' height=INT)? & ('url=' url=STRING) &
('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)* ']'))? &
Expand All @@ -102,7 +102,7 @@ Webview:
Switch:
'Switch' (('item=' item=ItemRef) & ('label=' label=(ID | STRING))? &
(('icon=' icon=Icon) |
('icon=[' (dynamicIcon+=IconArray (',' dynamicIcon+=IconArray)* ']')) |
('icon=[' (dynamicIcon+=IconRule (',' dynamicIcon+=IconRule)*) ']') |
('staticIcon=' staticIcon=Icon))? &
('mappings=[' mappings+=Mapping (',' mappings+=Mapping)* ']')? &
('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)* ']'))? &
Expand All @@ -113,7 +113,7 @@ Switch:
Mapview:
'Mapview' (('item=' item=ItemRef) & ('label=' label=(ID | STRING))? &
(('icon=' icon=Icon) |
('icon=[' (dynamicIcon+=IconArray (',' dynamicIcon+=IconArray)* ']')) |
('icon=[' (dynamicIcon+=IconRule (',' dynamicIcon+=IconRule)*) ']') |
('staticIcon=' staticIcon=Icon))? &
('height=' height=INT)? &
('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)* ']'))? &
Expand All @@ -124,7 +124,7 @@ Mapview:
Slider:
'Slider' (('item=' item=ItemRef) & ('label=' label=(ID | STRING))? &
(('icon=' icon=Icon) |
('icon=[' (dynamicIcon+=IconArray (',' dynamicIcon+=IconArray)* ']')) |
('icon=[' (dynamicIcon+=IconRule (',' dynamicIcon+=IconRule)*) ']') |
('staticIcon=' staticIcon=Icon))? &
('sendFrequency=' frequency=INT)? & (switchEnabled?='switchSupport')? &
('minValue=' minValue=Number)? & ('maxValue=' maxValue=Number)? & ('step=' step=Number)? &
Expand All @@ -136,7 +136,7 @@ Slider:
Selection:
'Selection' (('item=' item=ItemRef) & ('label=' label=(ID | STRING))? &
(('icon=' icon=Icon) |
('icon=[' (dynamicIcon+=IconArray (',' dynamicIcon+=IconArray)* ']')) |
('icon=[' (dynamicIcon+=IconRule (',' dynamicIcon+=IconRule)*) ']') |
('staticIcon=' staticIcon=Icon))? &
('mappings=[' mappings+=Mapping (',' mappings+=Mapping)* ']')? &
('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)* ']'))? &
Expand All @@ -147,7 +147,7 @@ Selection:
Setpoint:
'Setpoint' (('item=' item=ItemRef) & ('label=' label=(ID | STRING))? &
(('icon=' icon=Icon) |
('icon=[' (dynamicIcon+=IconArray (',' dynamicIcon+=IconArray)* ']')) |
('icon=[' (dynamicIcon+=IconRule (',' dynamicIcon+=IconRule)*) ']') |
('staticIcon=' staticIcon=Icon))? &
('minValue=' minValue=Number)? & ('maxValue=' maxValue=Number)? & ('step=' step=Number)? &
('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)* ']'))? &
Expand All @@ -158,7 +158,7 @@ Setpoint:
Colorpicker:
'Colorpicker' (('item=' item=ItemRef) & ('label=' label=(ID | STRING))? &
(('icon=' icon=Icon) |
('icon=[' (dynamicIcon+=IconArray (',' dynamicIcon+=IconArray)* ']')) |
('icon=[' (dynamicIcon+=IconRule (',' dynamicIcon+=IconRule)*) ']') |
('staticIcon=' staticIcon=Icon))? &
('sendFrequency=' frequency=INT)? &
('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)* ']'))? &
Expand All @@ -169,7 +169,7 @@ Colorpicker:
Input:
'Input' (('item=' item=ItemRef) & ('label=' label=(ID | STRING))? &
(('icon=' icon=Icon) |
('icon=[' (dynamicIcon+=IconArray (',' dynamicIcon+=IconArray)* ']')) |
('icon=[' (dynamicIcon+=IconRule (',' dynamicIcon+=IconRule)*) ']') |
('staticIcon=' staticIcon=Icon))? &
('inputHint=' inputHint=STRING)? &
('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)* ']'))? &
Expand All @@ -180,7 +180,7 @@ Input:
Default:
'Default' (('item=' item=ItemRef) & ('label=' label=(ID | STRING))? &
(('icon=' icon=Icon) |
('icon=[' (dynamicIcon+=IconArray (',' dynamicIcon+=IconArray)* ']')) |
('icon=[' (dynamicIcon+=IconRule (',' dynamicIcon+=IconRule)*) ']') |
('staticIcon=' staticIcon=Icon))? &
('height=' height=INT)? &
('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)* ']'))? &
Expand Down Expand Up @@ -208,12 +208,13 @@ IconName:
(ID '-')* ID;

ColorArray:
((item=ID)? (condition=('==' | '>' | '<' | '>=' | '<=' | '!='))? (sign=('-' | '+'))? (state=XState) '=')?
(arg=STRING);
((conditions+=Condition ('AND' conditions+=Condition)*) '=')? (arg=STRING);

IconArray:
((item=ID)? (condition=('==' | '>' | '<' | '>=' | '<=' | '!='))? (sign=('-' | '+'))? (state=XState) '=')?
(arg=Icon);
IconRule:
((conditions+=Condition ('AND' conditions+=Condition)*) '=')? (arg=Icon);

Condition:
(item=ID)? (condition=('==' | '>' | '<' | '>=' | '<=' | '!='))? (sign=('-' | '+'))? (state=XState);

Command returns ecore::EString:
Number | ID | STRING;
Expand Down
Loading

0 comments on commit fb0ab17

Please sign in to comment.