diff --git a/bundles/org.openhab.core.io.rest.core/pom.xml b/bundles/org.openhab.core.io.rest.core/pom.xml
index c2c00dd8f89..b9dc8976942 100644
--- a/bundles/org.openhab.core.io.rest.core/pom.xml
+++ b/bundles/org.openhab.core.io.rest.core/pom.xml
@@ -65,6 +65,21 @@
org.openhab.core.semantics
${project.version}
+
+ org.openhab.core.bundles
+ org.openhab.core.model.core
+ ${project.version}
+
+
+ org.openhab.core.bundles
+ org.openhab.core.model.sitemap
+ ${project.version}
+
+
+ org.openhab.core.bom
+ org.openhab.core.bom.compile-model
+ pom
+
org.openhab.core.bundles
org.openhab.core.test
@@ -72,5 +87,4 @@
test
-
diff --git a/bundles/org.openhab.core.io.rest.core/src/main/java/org/openhab/core/io/rest/core/internal/fileformat/FileFormatResource.java b/bundles/org.openhab.core.io.rest.core/src/main/java/org/openhab/core/io/rest/core/internal/fileformat/FileFormatResource.java
index ce91d748fda..6d72e6076ea 100644
--- a/bundles/org.openhab.core.io.rest.core/src/main/java/org/openhab/core/io/rest/core/internal/fileformat/FileFormatResource.java
+++ b/bundles/org.openhab.core.io.rest.core/src/main/java/org/openhab/core/io/rest/core/internal/fileformat/FileFormatResource.java
@@ -18,9 +18,11 @@
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
@@ -57,6 +59,9 @@
import org.openhab.core.items.MetadataKey;
import org.openhab.core.items.MetadataRegistry;
import org.openhab.core.items.fileconverter.ItemFileGenerator;
+import org.openhab.core.model.sitemap.SitemapProvider;
+import org.openhab.core.model.sitemap.fileconverter.SitemapFileGenerator;
+import org.openhab.core.model.sitemap.sitemap.Sitemap;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingRegistry;
@@ -92,12 +97,13 @@
/**
* This class acts as a REST resource and provides different methods to generate file format
- * for existing items and things.
+ * for existing items, things and sitemaps.
*
* This resource is registered with the Jersey servlet.
*
* @author Laurent Garnier - Initial contribution
* @author Laurent Garnier - Add YAML output for things
+ * @author Mark Herwege - Add sitemap DSL
*/
@Component
@JaxrsResource
@@ -175,6 +181,26 @@ public class FileFormatResource implements RESTResource {
param: my param value
""";
+ private static final String DSL_SITEMAPS_EXAMPLE = """
+ sitemap MySitemap label="My Sitemap" {
+ Frame {
+ Input item=MyItem label="My Input"
+ }
+ }
+ """;
+
+ private static final String YAML_SITEMAPS_EXAMPLE = """
+ version: 2
+ sitemaps:
+ MySitemap:
+ label: Label
+ widgets:
+ MyWidget:
+ type: Switch
+ label: Label
+ item: MyItem
+ """;
+
private final Logger logger = LoggerFactory.getLogger(FileFormatResource.class);
private final ItemRegistry itemRegistry;
@@ -184,8 +210,10 @@ public class FileFormatResource implements RESTResource {
private final Inbox inbox;
private final ThingTypeRegistry thingTypeRegistry;
private final ConfigDescriptionRegistry configDescRegistry;
+ private final List sitemapProviders = new ArrayList<>();
private final Map itemFileGenerators = new ConcurrentHashMap<>();
private final Map thingFileGenerators = new ConcurrentHashMap<>();
+ private final Map sitemapFileGenerators = new ConcurrentHashMap<>();
@Activate
public FileFormatResource(//
@@ -227,6 +255,24 @@ protected void removeThingFileGenerator(ThingFileGenerator thingFileGenerator) {
thingFileGenerators.remove(thingFileGenerator.getFileFormatGenerator());
}
+ @Reference(policy = ReferencePolicy.DYNAMIC, cardinality = ReferenceCardinality.MULTIPLE)
+ protected void addSitemapFileGenerator(SitemapFileGenerator sitemapFileGenerator) {
+ sitemapFileGenerators.put(sitemapFileGenerator.getFileFormatGenerator(), sitemapFileGenerator);
+ }
+
+ protected void removeSitemapFileGenerator(SitemapFileGenerator sitemapFileGenerator) {
+ sitemapFileGenerators.remove(sitemapFileGenerator.getFileFormatGenerator());
+ }
+
+ @Reference(policy = ReferencePolicy.DYNAMIC, cardinality = ReferenceCardinality.MULTIPLE)
+ protected void addSitemapProvider(SitemapProvider sitemapProvider) {
+ sitemapProviders.add(sitemapProvider);
+ }
+
+ protected void removeSitemapProvider(SitemapProvider sitemapProvider) {
+ sitemapProviders.remove(sitemapProvider);
+ }
+
@POST
@RolesAllowed({ Role.ADMIN })
@Path("/items")
@@ -305,6 +351,55 @@ public Response createFileFormatForThings(final @Context HttpHeaders httpHeaders
return Response.ok(new String(outputStream.toByteArray())).build();
}
+ @POST
+ @RolesAllowed({ Role.ADMIN })
+ @Path("/sitemaps")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces({ "text/vnd.openhab.dsl.sitemap", "application/yaml" })
+ @Operation(operationId = "createFileFormatForSitemaps", summary = "Create file format for a list of sitemaps in registry.", security = {
+ @SecurityRequirement(name = "oauth2", scopes = { "admin" }) }, responses = {
+ @ApiResponse(responseCode = "200", description = "OK", content = {
+ @Content(mediaType = "text/vnd.openhab.dsl.sitemap", schema = @Schema(example = DSL_SITEMAPS_EXAMPLE)),
+ @Content(mediaType = "application/yaml", schema = @Schema(example = YAML_SITEMAPS_EXAMPLE)) }),
+ @ApiResponse(responseCode = "404", description = "One or more sitemaps not found in registry."),
+ @ApiResponse(responseCode = "415", description = "Unsupported media type.") })
+ public Response createFileFormatForSitemaps(final @Context HttpHeaders httpHeaders,
+ @Parameter(description = "Array of Sitemap UIDs. If empty or omitted, return all Sitemaps from the Registry.") @Nullable List sitemapUIDs) {
+ String acceptHeader = httpHeaders.getHeaderString(HttpHeaders.ACCEPT);
+ logger.debug("createFileFormatForSitemaps: mediaType = {}, sitemapUIDs = {}", acceptHeader, sitemapUIDs);
+ SitemapFileGenerator generator = getSitemapFileGenerator(acceptHeader);
+ if (generator == null) {
+ return Response.status(Response.Status.UNSUPPORTED_MEDIA_TYPE)
+ .entity("Unsupported media type '" + acceptHeader + "'!").build();
+ }
+ Collection sitemapNames;
+ Map allSitemapNames = sitemapProviders.stream()
+ .flatMap(provider -> provider.getSitemapNames().stream().map(name -> Map.entry(name, provider)))
+ .sorted(Comparator.comparing(Map.Entry::getKey))
+ .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (existing, replacement) -> existing));
+ if (sitemapUIDs == null || sitemapUIDs.isEmpty()) {
+ sitemapNames = allSitemapNames.keySet();
+ } else if (allSitemapNames.keySet().containsAll(sitemapUIDs)) {
+ sitemapNames = sitemapUIDs;
+ } else {
+ String sitemapUID = sitemapUIDs.stream().filter(name -> !allSitemapNames.keySet().contains(name))
+ .findFirst().get();
+ return Response.status(Response.Status.NOT_FOUND)
+ .entity("Sitemap with UID '" + sitemapUID + "' does not exist!").build();
+ }
+ List sitemaps = sitemapNames.stream().sorted().map(name -> {
+ SitemapProvider provider = allSitemapNames.get(name);
+ if (provider == null) {
+ return null;
+ }
+ return provider.getSitemap(name);
+ }).filter(Objects::nonNull).toList();
+
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ generator.generateFileFormat(outputStream, sitemaps);
+ return Response.ok(new String(outputStream.toByteArray())).build();
+ }
+
/*
* Get all the metadata for a list of items including channel links mapped to metadata in the namespace "channel"
*/
@@ -465,6 +560,14 @@ private Thing simulateThing(DiscoveryResult result, ThingType thingType) {
};
}
+ private @Nullable SitemapFileGenerator getSitemapFileGenerator(String mediaType) {
+ return switch (mediaType) {
+ case "text/vnd.openhab.dsl.sitemap" -> sitemapFileGenerators.get("DSL");
+ case "application/yaml" -> sitemapFileGenerators.get("YAML");
+ default -> null;
+ };
+ }
+
private List getThingsOrDiscoveryResult(List thingUIDs) {
return thingUIDs.stream().distinct().map(uid -> {
ThingUID thingUID = new ThingUID(uid);
diff --git a/bundles/org.openhab.core.io.rest.sitemap/src/main/java/org/openhab/core/io/rest/sitemap/internal/SitemapResource.java b/bundles/org.openhab.core.io.rest.sitemap/src/main/java/org/openhab/core/io/rest/sitemap/internal/SitemapResource.java
index d9ad389c8d2..74c7e169fa0 100644
--- a/bundles/org.openhab.core.io.rest.sitemap/src/main/java/org/openhab/core/io/rest/sitemap/internal/SitemapResource.java
+++ b/bundles/org.openhab.core.io.rest.sitemap/src/main/java/org/openhab/core/io/rest/sitemap/internal/SitemapResource.java
@@ -82,13 +82,16 @@
import org.openhab.core.model.sitemap.SitemapProvider;
import org.openhab.core.model.sitemap.sitemap.Button;
import org.openhab.core.model.sitemap.sitemap.ButtonDefinition;
+import org.openhab.core.model.sitemap.sitemap.ButtonDefinitionList;
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.ColorArrayList;
import org.openhab.core.model.sitemap.sitemap.Colortemperaturepicker;
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.IconRuleList;
import org.openhab.core.model.sitemap.sitemap.Image;
import org.openhab.core.model.sitemap.sitemap.Input;
import org.openhab.core.model.sitemap.sitemap.LinkableWidget;
@@ -101,6 +104,7 @@
import org.openhab.core.model.sitemap.sitemap.Switch;
import org.openhab.core.model.sitemap.sitemap.Video;
import org.openhab.core.model.sitemap.sitemap.VisibilityRule;
+import org.openhab.core.model.sitemap.sitemap.VisibilityRuleList;
import org.openhab.core.model.sitemap.sitemap.Webview;
import org.openhab.core.model.sitemap.sitemap.Widget;
import org.openhab.core.types.State;
@@ -614,7 +618,8 @@ private PageDTO createPageBean(String sitemapName, @Nullable String title, @Null
}
bean.widgetId = widgetId;
bean.icon = itemUIRegistry.getCategory(widget);
- bean.staticIcon = widget.getStaticIcon() != null || !widget.getIconRules().isEmpty();
+ bean.staticIcon = widget.getStaticIcon() != null
+ || (widget.getIconRules() != null && !widget.getIconRules().getElements().isEmpty());
bean.labelcolor = convertItemValueColor(itemUIRegistry.getLabelColor(widget), itemState);
bean.valuecolor = convertItemValueColor(itemUIRegistry.getValueColor(widget), itemState);
bean.iconcolor = convertItemValueColor(itemUIRegistry.getIconColor(widget), itemState);
@@ -642,8 +647,8 @@ private PageDTO createPageBean(String sitemapName, @Nullable String title, @Null
isLeaf(children), uri, locale, false, evenIfHidden);
}
}
- if (widget instanceof Switch switchWidget) {
- for (Mapping mapping : switchWidget.getMappings()) {
+ if (widget instanceof Switch switchWidget && switchWidget.getMappings() != null) {
+ for (Mapping mapping : switchWidget.getMappings().getElements()) {
MappingDTO mappingBean = new MappingDTO();
mappingBean.command = mapping.getCmd();
mappingBean.releaseCommand = mapping.getReleaseCmd();
@@ -652,8 +657,8 @@ private PageDTO createPageBean(String sitemapName, @Nullable String title, @Null
bean.mappings.add(mappingBean);
}
}
- if (widget instanceof Selection selectionWidget) {
- for (Mapping mapping : selectionWidget.getMappings()) {
+ if (widget instanceof Selection selectionWidget && selectionWidget.getMappings() != null) {
+ for (Mapping mapping : selectionWidget.getMappings().getElements()) {
MappingDTO mappingBean = new MappingDTO();
mappingBean.command = mapping.getCmd();
mappingBean.label = mapping.getLabel();
@@ -714,19 +719,23 @@ private PageDTO createPageBean(String sitemapName, @Nullable String title, @Null
bean.maxValue = colortemperaturepickerWidget.getMaxValue();
}
if (widget instanceof Buttongrid buttonGridWidget) {
- for (ButtonDefinition button : buttonGridWidget.getButtons()) {
- MappingDTO mappingBean = new MappingDTO();
- mappingBean.row = button.getRow();
- mappingBean.column = button.getColumn();
- mappingBean.command = button.getCmd();
- mappingBean.label = button.getLabel();
- mappingBean.icon = button.getIcon();
- bean.mappings.add(mappingBean);
+ ButtonDefinitionList buttonDefinitionList = buttonGridWidget.getButtons();
+ if (buttonDefinitionList != null) {
+ for (ButtonDefinition button : buttonDefinitionList.getElements()) {
+ MappingDTO mappingBean = new MappingDTO();
+ mappingBean.row = button.getRow();
+ mappingBean.column = button.getColumn();
+ mappingBean.command = button.getCmd();
+ mappingBean.label = button.getLabel();
+ mappingBean.icon = button.getIcon();
+ bean.mappings.add(mappingBean);
+ }
}
}
if (widget instanceof Button buttonWidget) {
// Get the icon from the widget only
- if (widget.getIcon() == null && widget.getStaticIcon() == null && widget.getIconRules().isEmpty()) {
+ if (widget.getIcon() == null && widget.getStaticIcon() == null
+ && (widget.getIconRules() == null || widget.getIconRules().getElements().isEmpty())) {
bean.icon = null;
bean.staticIcon = null;
}
@@ -877,26 +886,32 @@ private Set getAllItems(List widgets) {
return items;
}
- private Set getItemsInVisibilityCond(EList ruleList) {
+ private Set getItemsInVisibilityCond(@Nullable VisibilityRuleList ruleList) {
Set items = new HashSet<>();
- for (VisibilityRule rule : ruleList) {
- getItemsInConditions(rule.getConditions(), items);
+ if (ruleList != null) {
+ for (VisibilityRule rule : ruleList.getElements()) {
+ getItemsInConditions(rule.getConditions(), items);
+ }
}
return items;
}
- private Set getItemsInColorCond(EList colorList) {
+ private Set getItemsInColorCond(@Nullable ColorArrayList colorList) {
Set items = new HashSet<>();
- for (ColorArray rule : colorList) {
- getItemsInConditions(rule.getConditions(), items);
+ if (colorList != null) {
+ for (ColorArray rule : colorList.getElements()) {
+ getItemsInConditions(rule.getConditions(), items);
+ }
}
return items;
}
- private Set getItemsInIconCond(EList ruleList) {
+ private Set getItemsInIconCond(@Nullable IconRuleList ruleList) {
Set items = new HashSet<>();
- for (IconRule rule : ruleList) {
- getItemsInConditions(rule.getConditions(), items);
+ if (ruleList != null) {
+ for (IconRule rule : ruleList.getElements()) {
+ getItemsInConditions(rule.getConditions(), items);
+ }
}
return items;
}
diff --git a/bundles/org.openhab.core.io.rest.sitemap/src/main/java/org/openhab/core/io/rest/sitemap/internal/WidgetsChangeListener.java b/bundles/org.openhab.core.io.rest.sitemap/src/main/java/org/openhab/core/io/rest/sitemap/internal/WidgetsChangeListener.java
index 6bbb3173ffd..327cd6a2b68 100644
--- a/bundles/org.openhab.core.io.rest.sitemap/src/main/java/org/openhab/core/io/rest/sitemap/internal/WidgetsChangeListener.java
+++ b/bundles/org.openhab.core.io.rest.sitemap/src/main/java/org/openhab/core/io/rest/sitemap/internal/WidgetsChangeListener.java
@@ -40,10 +40,13 @@
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.ColorArrayList;
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.IconRuleList;
import org.openhab.core.model.sitemap.sitemap.VisibilityRule;
+import org.openhab.core.model.sitemap.sitemap.VisibilityRuleList;
import org.openhab.core.model.sitemap.sitemap.Widget;
import org.openhab.core.types.State;
import org.openhab.core.ui.items.ItemUIRegistry;
@@ -135,24 +138,39 @@ private Set- getAllItems(EList widgets) {
items.addAll(getAllItems(grid.getChildren()));
}
// now scan icon rules
- for (IconRule rule : widget.getIconRules()) {
- addItemsFromConditions(items, rule.getConditions());
+ IconRuleList iconRuleList = widget.getIconRules();
+ if (iconRuleList != null) {
+ for (IconRule rule : iconRuleList.getElements()) {
+ addItemsFromConditions(items, rule.getConditions());
+ }
}
// now scan visibility rules
- for (VisibilityRule rule : widget.getVisibility()) {
- addItemsFromConditions(items, rule.getConditions());
+ VisibilityRuleList visibilityRuleList = widget.getVisibility();
+ if (visibilityRuleList != null) {
+ for (VisibilityRule rule : visibilityRuleList.getElements()) {
+ addItemsFromConditions(items, rule.getConditions());
+ }
}
// now scan label color rules
- for (ColorArray rule : widget.getLabelColor()) {
- addItemsFromConditions(items, rule.getConditions());
+ ColorArrayList labelColorArrayList = widget.getLabelColor();
+ if (labelColorArrayList != null) {
+ for (ColorArray rule : labelColorArrayList.getElements()) {
+ addItemsFromConditions(items, rule.getConditions());
+ }
}
// now scan value color rules
- for (ColorArray rule : widget.getValueColor()) {
- addItemsFromConditions(items, rule.getConditions());
+ ColorArrayList valueColorArrayList = widget.getValueColor();
+ if (valueColorArrayList != null) {
+ for (ColorArray rule : valueColorArrayList.getElements()) {
+ addItemsFromConditions(items, rule.getConditions());
+ }
}
// now scan icon color rules
- for (ColorArray rule : widget.getIconColor()) {
- addItemsFromConditions(items, rule.getConditions());
+ ColorArrayList iconColorArrayList = widget.getIconColor();
+ if (iconColorArrayList != null) {
+ for (ColorArray rule : iconColorArrayList.getElements()) {
+ addItemsFromConditions(items, rule.getConditions());
+ }
}
}
}
@@ -231,7 +249,8 @@ private SitemapWidgetEvent constructSitemapEventForWidget(Item item, State state
event.reloadIcon = widget.getStaticIcon() == null;
if (widget instanceof Button buttonWidget) {
// Get the icon from the widget only
- if (widget.getIcon() == null && widget.getStaticIcon() == null && widget.getIconRules().isEmpty()) {
+ if (widget.getIcon() == null && widget.getStaticIcon() == null
+ && (widget.getIconRules() == null || widget.getIconRules().getElements().isEmpty())) {
event.icon = null;
event.reloadIcon = false;
}
@@ -281,11 +300,16 @@ private Item getItemForWidget(Widget w) {
}
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.getIconRules().stream().anyMatch(r -> conditionsDependsOnItem(r.getConditions(), name));
+ return (w.getVisibility() != null && w.getVisibility().getElements().stream()
+ .anyMatch(r -> conditionsDependsOnItem(r.getConditions(), name)))
+ || (w.getLabelColor() != null && w.getLabelColor().getElements().stream()
+ .anyMatch(r -> conditionsDependsOnItem(r.getConditions(), name)))
+ || (w.getValueColor() != null && w.getValueColor().getElements().stream()
+ .anyMatch(r -> conditionsDependsOnItem(r.getConditions(), name)))
+ || (w.getIconColor() != null && w.getIconColor().getElements().stream()
+ .anyMatch(r -> conditionsDependsOnItem(r.getConditions(), name)))
+ || (w.getIconRules() != null && w.getIconRules().getElements().stream()
+ .anyMatch(r -> conditionsDependsOnItem(r.getConditions(), name)));
}
private boolean conditionsDependsOnItem(@Nullable EList conditions, String name) {
diff --git a/bundles/org.openhab.core.io.rest.sitemap/src/test/java/org/openhab/core/io/rest/sitemap/internal/SitemapResourceTest.java b/bundles/org.openhab.core.io.rest.sitemap/src/test/java/org/openhab/core/io/rest/sitemap/internal/SitemapResourceTest.java
index 9c7413f1287..1aead0e7f0b 100644
--- a/bundles/org.openhab.core.io.rest.sitemap/src/test/java/org/openhab/core/io/rest/sitemap/internal/SitemapResourceTest.java
+++ b/bundles/org.openhab.core.io.rest.sitemap/src/test/java/org/openhab/core/io/rest/sitemap/internal/SitemapResourceTest.java
@@ -16,8 +16,7 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
import static org.hamcrest.collection.IsEmptyCollection.empty;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.*;
import java.util.Collection;
import java.util.Collections;
@@ -53,11 +52,14 @@
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.ColorArrayList;
import org.openhab.core.model.sitemap.sitemap.Condition;
import org.openhab.core.model.sitemap.sitemap.Group;
import org.openhab.core.model.sitemap.sitemap.IconRule;
+import org.openhab.core.model.sitemap.sitemap.IconRuleList;
import org.openhab.core.model.sitemap.sitemap.Sitemap;
import org.openhab.core.model.sitemap.sitemap.VisibilityRule;
+import org.openhab.core.model.sitemap.sitemap.VisibilityRuleList;
import org.openhab.core.model.sitemap.sitemap.Widget;
import org.openhab.core.test.java.JavaTest;
import org.openhab.core.types.Command;
@@ -621,11 +623,21 @@ private static void mockWidgetMethods(EList iconRules1, EList items,
model.getItems().add(buildModelItem(item, getChannelLinks(metadata, item.getName()),
getMetadata(metadata, item.getName()), hideDefaultParameters));
}
- modelRepository.generateSyntaxFromModel(out, "items", model);
+ modelRepository.generateSyntaxFromModel(out, "items", model, serializer);
}
private ModelItem buildModelItem(Item item, List channelLinks, List metadata,
diff --git a/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/Sitemap.xtext b/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/Sitemap.xtext
index a89e623c9df..d01175cc7da 100644
--- a/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/Sitemap.xtext
+++ b/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/Sitemap.xtext
@@ -24,207 +24,190 @@ LinkableWidget:
'}')?;
Frame:
- {Frame} 'Frame' (('item=' item=ItemRef)? & ('label=' label=(ID | STRING))? &
- (('icon=' icon=Icon) |
- ('icon=[' (IconRules+=IconRule (',' IconRules+=IconRule)*) ']') |
- ('staticIcon=' staticIcon=Icon))? &
- ('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)*) ']')? &
- ('valuecolor=[' (ValueColor+=ColorArray (',' ValueColor+=ColorArray)*) ']')? &
- ('iconcolor=[' (IconColor+=ColorArray (',' IconColor+=ColorArray)*) ']')? &
- ('visibility=[' (Visibility+=VisibilityRule (',' Visibility+=VisibilityRule)*) ']')?);
-
+ {Frame} 'Frame' (('item=' item=ItemRef) | ('label=' label=(ID | STRING)) |
+ ('icon=' icon=Icon) | ('icon=' IconRules=IconRuleList) | ('staticIcon=' staticIcon=Icon) |
+ ('labelcolor=' labelColor=ColorArrayList) |
+ ('valuecolor=' valueColor=ColorArrayList) |
+ ('iconcolor=' iconColor=ColorArrayList) |
+ ('visibility=' visibility=VisibilityRuleList))*;
Text:
- {Text} 'Text' (('item=' item=ItemRef)? & ('label=' label=(ID | STRING))? &
- (('icon=' icon=Icon) |
- ('icon=[' (IconRules+=IconRule (',' IconRules+=IconRule)*) ']') |
- ('staticIcon=' staticIcon=Icon))? &
- ('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)*) ']')? &
- ('valuecolor=[' (ValueColor+=ColorArray (',' ValueColor+=ColorArray)*) ']')? &
- ('iconcolor=[' (IconColor+=ColorArray (',' IconColor+=ColorArray)*) ']')? &
- ('visibility=[' (Visibility+=VisibilityRule (',' Visibility+=VisibilityRule)*) ']')?);
+ {Text} 'Text' (('item=' item=ItemRef) | ('label=' label=(ID | STRING)) |
+ ('icon=' icon=Icon) | ('icon=' IconRules=IconRuleList) | ('staticIcon=' staticIcon=Icon) |
+ ('labelcolor=' labelColor=ColorArrayList) |
+ ('valuecolor=' valueColor=ColorArrayList) |
+ ('iconcolor=' iconColor=ColorArrayList) |
+ ('visibility=' visibility=VisibilityRuleList))*;
Group:
- 'Group' (('item=' item=GroupItemRef) & ('label=' label=(ID | STRING))? &
- (('icon=' icon=Icon) |
- ('icon=[' (IconRules+=IconRule (',' IconRules+=IconRule)*) ']') |
- ('staticIcon=' staticIcon=Icon))? &
- ('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)*) ']')? &
- ('valuecolor=[' (ValueColor+=ColorArray (',' ValueColor+=ColorArray)*) ']')? &
- ('iconcolor=[' (IconColor+=ColorArray (',' IconColor+=ColorArray)*) ']')? &
- ('visibility=[' (Visibility+=VisibilityRule (',' Visibility+=VisibilityRule)*) ']')?);
+ 'Group' (('item=' item=GroupItemRef) | ('label=' label=(ID | STRING)) |
+ ('icon=' icon=Icon) | ('icon=' IconRules=IconRuleList) | ('staticIcon=' staticIcon=Icon) |
+ ('labelcolor=' labelColor=ColorArrayList) |
+ ('valuecolor=' valueColor=ColorArrayList) |
+ ('iconcolor=' iconColor=ColorArrayList) |
+ ('visibility=' visibility=VisibilityRuleList))*;
Image:
- 'Image' (('item=' item=ItemRef)? & ('label=' label=(ID | STRING))? &
- (('icon=' icon=Icon) |
- ('icon=[' (IconRules+=IconRule (',' IconRules+=IconRule)*) ']') |
- ('staticIcon=' staticIcon=Icon))? &
- ('url=' url=STRING)? & ('refresh=' refresh=INT)? &
- ('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)*) ']')? &
- ('valuecolor=[' (ValueColor+=ColorArray (',' ValueColor+=ColorArray)*) ']')? &
- ('iconcolor=[' (IconColor+=ColorArray (',' IconColor+=ColorArray)*) ']')? &
- ('visibility=[' (Visibility+=VisibilityRule (',' Visibility+=VisibilityRule)*) ']')?);
+ 'Image' (('item=' item=ItemRef) | ('label=' label=(ID | STRING)) |
+ ('icon=' icon=Icon) | ('icon=' IconRules=IconRuleList) | ('staticIcon=' staticIcon=Icon) |
+ ('url=' url=STRING) | ('refresh=' refresh=INT) |
+ ('labelcolor=' labelColor=ColorArrayList) |
+ ('valuecolor=' valueColor=ColorArrayList) |
+ ('iconcolor=' iconColor=ColorArrayList) |
+ ('visibility=' visibility=VisibilityRuleList))*;
Video:
- 'Video' (('item=' item=ItemRef)? & ('label=' label=(ID | STRING))? &
- (('icon=' icon=Icon) |
- ('icon=[' (IconRules+=IconRule (',' IconRules+=IconRule)*) ']') |
- ('staticIcon=' staticIcon=Icon))? &
- ('url=' url=STRING) & ('encoding=' encoding=STRING)? &
- ('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)*) ']')? &
- ('valuecolor=[' (ValueColor+=ColorArray (',' ValueColor+=ColorArray)*) ']')? &
- ('iconcolor=[' (IconColor+=ColorArray (',' IconColor+=ColorArray)*) ']')? &
- ('visibility=[' (Visibility+=VisibilityRule (',' Visibility+=VisibilityRule)*) ']')?);
+ 'Video' (('item=' item=ItemRef) | ('label=' label=(ID | STRING)) |
+ ('icon=' icon=Icon) | ('icon=' IconRules=IconRuleList) | ('staticIcon=' staticIcon=Icon) |
+ ('url=' url=STRING) | ('encoding=' encoding=STRING) |
+ ('labelcolor=' labelColor=ColorArrayList) |
+ ('valuecolor=' valueColor=ColorArrayList) |
+ ('iconcolor=' iconColor=ColorArrayList) |
+ ('visibility=' visibility=VisibilityRuleList))*;
Chart:
- 'Chart' (('item=' item=ItemRef) & ('label=' label=(ID | STRING))? &
- (('icon=' icon=Icon) |
- ('icon=[' (IconRules+=IconRule (',' IconRules+=IconRule)*) ']') |
- ('staticIcon=' staticIcon=Icon))? &
- ('service=' service=STRING)? & ('refresh=' refresh=INT)? & ('period=' period=Period) &
- ('legend=' legend=BOOLEAN_OBJECT)? & ('forceasitem=' forceAsItem=BOOLEAN_OBJECT)? &
- ('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)*) ']')? &
- ('valuecolor=[' (ValueColor+=ColorArray (',' ValueColor+=ColorArray)*) ']')? &
- ('iconcolor=[' (IconColor+=ColorArray (',' IconColor+=ColorArray)*) ']')? &
- ('visibility=[' (Visibility+=VisibilityRule (',' Visibility+=VisibilityRule)*) ']')? &
- ('yAxisDecimalPattern=' yAxisDecimalPattern=(STRING))? &
- ('interpolation=' interpolation=(STRING))?);
+ 'Chart' (('item=' item=ItemRef) | ('label=' label=(ID | STRING)) |
+ ('icon=' icon=Icon) | ('icon=' IconRules=IconRuleList) | ('staticIcon=' staticIcon=Icon) |
+ ('service=' service=STRING) | ('refresh=' refresh=INT) | ('period=' period=Period) |
+ ('legend=' legend=BOOLEAN_OBJECT) | ('forceasitem=' forceAsItem=BOOLEAN_OBJECT) |
+ ('yAxisDecimalPattern=' yAxisDecimalPattern=(STRING)) |
+ ('interpolation=' interpolation=(STRING)) |
+ ('labelcolor=' labelColor=ColorArrayList) |
+ ('valuecolor=' valueColor=ColorArrayList) |
+ ('iconcolor=' iconColor=ColorArrayList) |
+ ('visibility=' visibility=VisibilityRuleList))*;
Webview:
- 'Webview' (('item=' item=ItemRef)? & ('label=' label=(ID | STRING))? &
- (('icon=' icon=Icon) |
- ('icon=[' (IconRules+=IconRule (',' IconRules+=IconRule)*) ']') |
- ('staticIcon=' staticIcon=Icon))? &
- ('height=' height=INT)? & ('url=' url=STRING) &
- ('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)*) ']')? &
- ('valuecolor=[' (ValueColor+=ColorArray (',' ValueColor+=ColorArray)*) ']')? &
- ('iconcolor=[' (IconColor+=ColorArray (',' IconColor+=ColorArray)*) ']')? &
- ('visibility=[' (Visibility+=VisibilityRule (',' Visibility+=VisibilityRule)*) ']')?);
+ 'Webview' (('item=' item=ItemRef) | ('label=' label=(ID | STRING)) |
+ ('icon=' icon=Icon) | ('icon=' IconRules=IconRuleList) | ('staticIcon=' staticIcon=Icon) |
+ ('height=' height=INT) | ('url=' url=STRING) |
+ ('labelcolor=' labelColor=ColorArrayList) |
+ ('valuecolor=' valueColor=ColorArrayList) |
+ ('iconcolor=' iconColor=ColorArrayList) |
+ ('visibility=' visibility=VisibilityRuleList))*;
Switch:
- 'Switch' (('item=' item=ItemRef) & ('label=' label=(ID | STRING))? &
- (('icon=' icon=Icon) |
- ('icon=[' (IconRules+=IconRule (',' IconRules+=IconRule)*) ']') |
- ('staticIcon=' staticIcon=Icon))? &
- ('mappings=[' mappings+=Mapping (',' mappings+=Mapping)* ']')? &
- ('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)*) ']')? &
- ('valuecolor=[' (ValueColor+=ColorArray (',' ValueColor+=ColorArray)*) ']')? &
- ('iconcolor=[' (IconColor+=ColorArray (',' IconColor+=ColorArray)*) ']')? &
- ('visibility=[' (Visibility+=VisibilityRule (',' Visibility+=VisibilityRule)*) ']')?);
+ 'Switch' (('item=' item=ItemRef) | ('label=' label=(ID | STRING)) |
+ ('icon=' icon=Icon) | ('icon=' IconRules=IconRuleList) | ('staticIcon=' staticIcon=Icon) |
+ ('mappings=' mappings=MappingList) |
+ ('labelcolor=' labelColor=ColorArrayList) |
+ ('valuecolor=' valueColor=ColorArrayList) |
+ ('iconcolor=' iconColor=ColorArrayList) |
+ ('visibility=' visibility=VisibilityRuleList))*;
Mapview:
- 'Mapview' (('item=' item=ItemRef) & ('label=' label=(ID | STRING))? &
- (('icon=' icon=Icon) |
- ('icon=[' (IconRules+=IconRule (',' IconRules+=IconRule)*) ']') |
- ('staticIcon=' staticIcon=Icon))? &
- ('height=' height=INT)? &
- ('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)*) ']')? &
- ('valuecolor=[' (ValueColor+=ColorArray (',' ValueColor+=ColorArray)*) ']')? &
- ('iconcolor=[' (IconColor+=ColorArray (',' IconColor+=ColorArray)*) ']')? &
- ('visibility=[' (Visibility+=VisibilityRule (',' Visibility+=VisibilityRule)*) ']')?);
+ 'Mapview' (('item=' item=ItemRef) | ('label=' label=(ID | STRING)) |
+ ('icon=' icon=Icon) | ('icon=' IconRules=IconRuleList) | ('staticIcon=' staticIcon=Icon) |
+ ('height=' height=INT) |
+ ('labelcolor=' labelColor=ColorArrayList) |
+ ('valuecolor=' valueColor=ColorArrayList) |
+ ('iconcolor=' iconColor=ColorArrayList) |
+ ('visibility=' visibility=VisibilityRuleList))*;
Slider:
- 'Slider' (('item=' item=ItemRef) & ('label=' label=(ID | STRING))? &
- (('icon=' icon=Icon) |
- ('icon=[' (IconRules+=IconRule (',' IconRules+=IconRule)*) ']') |
- ('staticIcon=' staticIcon=Icon))? &
- (switchEnabled?='switchSupport')? & (releaseOnly?='releaseOnly')? &
- ('minValue=' minValue=Number)? & ('maxValue=' maxValue=Number)? & ('step=' step=Number)? &
- ('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)*) ']')? &
- ('valuecolor=[' (ValueColor+=ColorArray (',' ValueColor+=ColorArray)*) ']')? &
- ('iconcolor=[' (IconColor+=ColorArray (',' IconColor+=ColorArray)*) ']')? &
- ('visibility=[' (Visibility+=VisibilityRule (',' Visibility+=VisibilityRule)*) ']')?);
+ 'Slider' (('item=' item=ItemRef) | ('label=' label=(ID | STRING)) |
+ ('icon=' icon=Icon) | ('icon=' IconRules=IconRuleList) | ('staticIcon=' staticIcon=Icon) |
+ (switchEnabled?='switchSupport') | (releaseOnly?='releaseOnly') |
+ ('minValue=' minValue=Number) | ('maxValue=' maxValue=Number) | ('step=' step=Number) |
+ ('labelcolor=' labelColor=ColorArrayList) |
+ ('valuecolor=' valueColor=ColorArrayList) |
+ ('iconcolor=' iconColor=ColorArrayList) |
+ ('visibility=' visibility=VisibilityRuleList))*;
Selection:
- 'Selection' (('item=' item=ItemRef) & ('label=' label=(ID | STRING))? &
- (('icon=' icon=Icon) |
- ('icon=[' (IconRules+=IconRule (',' IconRules+=IconRule)*) ']') |
- ('staticIcon=' staticIcon=Icon))? &
- ('mappings=[' mappings+=Mapping (',' mappings+=Mapping)* ']')? &
- ('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)*) ']')? &
- ('valuecolor=[' (ValueColor+=ColorArray (',' ValueColor+=ColorArray)*) ']')? &
- ('iconcolor=[' (IconColor+=ColorArray (',' IconColor+=ColorArray)*) ']')? &
- ('visibility=[' (Visibility+=VisibilityRule (',' Visibility+=VisibilityRule)*) ']')?);
+ 'Selection' (('item=' item=ItemRef) | ('label=' label=(ID | STRING)) |
+ ('icon=' icon=Icon) | ('icon=' IconRules=IconRuleList) | ('staticIcon=' staticIcon=Icon) |
+ ('mappings=' mappings=MappingList) |
+ ('labelcolor=' labelColor=ColorArrayList) |
+ ('valuecolor=' valueColor=ColorArrayList) |
+ ('iconcolor=' iconColor=ColorArrayList) |
+ ('visibility=' visibility=VisibilityRuleList))*;
Setpoint:
- 'Setpoint' (('item=' item=ItemRef) & ('label=' label=(ID | STRING))? &
- (('icon=' icon=Icon) |
- ('icon=[' (IconRules+=IconRule (',' IconRules+=IconRule)*) ']') |
- ('staticIcon=' staticIcon=Icon))? &
- ('minValue=' minValue=Number)? & ('maxValue=' maxValue=Number)? & ('step=' step=Number)? &
- ('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)*) ']')? &
- ('valuecolor=[' (ValueColor+=ColorArray (',' ValueColor+=ColorArray)*) ']')? &
- ('iconcolor=[' (IconColor+=ColorArray (',' IconColor+=ColorArray)*) ']')? &
- ('visibility=[' (Visibility+=VisibilityRule (',' Visibility+=VisibilityRule)*) ']')?);
+ 'Setpoint' (('item=' item=ItemRef) | ('label=' label=(ID | STRING)) |
+ ('icon=' icon=Icon) | ('icon=' IconRules=IconRuleList) | ('staticIcon=' staticIcon=Icon) |
+ ('minValue=' minValue=Number) | ('maxValue=' maxValue=Number) | ('step=' step=Number) |
+ ('labelcolor=' labelColor=ColorArrayList) |
+ ('valuecolor=' valueColor=ColorArrayList) |
+ ('iconcolor=' iconColor=ColorArrayList) |
+ ('visibility=' visibility=VisibilityRuleList))*;
Colorpicker:
- 'Colorpicker' (('item=' item=ItemRef) & ('label=' label=(ID | STRING))? &
- (('icon=' icon=Icon) |
- ('icon=[' (IconRules+=IconRule (',' IconRules+=IconRule)*) ']') |
- ('staticIcon=' staticIcon=Icon))? &
- ('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)*) ']')? &
- ('valuecolor=[' (ValueColor+=ColorArray (',' ValueColor+=ColorArray)*) ']')? &
- ('iconcolor=[' (IconColor+=ColorArray (',' IconColor+=ColorArray)*) ']')? &
- ('visibility=[' (Visibility+=VisibilityRule (',' Visibility+=VisibilityRule)*) ']')?);
+ 'Colorpicker' (('item=' item=ItemRef) | ('label=' label=(ID | STRING)) |
+ ('icon=' icon=Icon) | ('icon=' IconRules=IconRuleList) | ('staticIcon=' staticIcon=Icon) |
+ ('labelcolor=' labelColor=ColorArrayList) |
+ ('valuecolor=' valueColor=ColorArrayList) |
+ ('iconcolor=' iconColor=ColorArrayList) |
+ ('visibility=' visibility=VisibilityRuleList))*;
Colortemperaturepicker:
- 'Colortemperaturepicker' (('item=' item=ItemRef) & ('label=' label=(ID | STRING))? &
- (('icon=' icon=Icon) |
- ('icon=[' (IconRules+=IconRule (',' IconRules+=IconRule)*) ']') |
- ('staticIcon=' staticIcon=Icon))? &
- ('minValue=' minValue=Number)? & ('maxValue=' maxValue=Number)? &
- ('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)*) ']')? &
- ('valuecolor=[' (ValueColor+=ColorArray (',' ValueColor+=ColorArray)*) ']')? &
- ('iconcolor=[' (IconColor+=ColorArray (',' IconColor+=ColorArray)*) ']')? &
- ('visibility=[' (Visibility+=VisibilityRule (',' Visibility+=VisibilityRule)*) ']')?);
+ 'Colortemperaturepicker' (('item=' item=ItemRef) | ('label=' label=(ID | STRING)) |
+ ('icon=' icon=Icon) | ('icon=' IconRules=IconRuleList) | ('staticIcon=' staticIcon=Icon) |
+ ('minValue=' minValue=Number) | ('maxValue=' maxValue=Number) |
+ ('labelcolor=' labelColor=ColorArrayList) |
+ ('valuecolor=' valueColor=ColorArrayList) |
+ ('iconcolor=' iconColor=ColorArrayList) |
+ ('visibility=' visibility=VisibilityRuleList))*;
Input:
- 'Input' (('item=' item=ItemRef) & ('label=' label=(ID | STRING))? &
- (('icon=' icon=Icon) |
- ('icon=[' (IconRules+=IconRule (',' IconRules+=IconRule)*) ']') |
- ('staticIcon=' staticIcon=Icon))? &
- ('inputHint=' inputHint=STRING)? &
- ('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)*) ']')? &
- ('valuecolor=[' (ValueColor+=ColorArray (',' ValueColor+=ColorArray)*) ']')? &
- ('iconcolor=[' (IconColor+=ColorArray (',' IconColor+=ColorArray)*) ']')? &
- ('visibility=[' (Visibility+=VisibilityRule (',' Visibility+=VisibilityRule)*) ']')?);
+ 'Input' (('item=' item=ItemRef) | ('label=' label=(ID | STRING)) |
+ ('icon=' icon=Icon) | ('icon=' IconRules=IconRuleList) | ('staticIcon=' staticIcon=Icon) |
+ ('inputHint=' inputHint=STRING) |
+ ('labelcolor=' labelColor=ColorArrayList) |
+ ('valuecolor=' valueColor=ColorArrayList) |
+ ('iconcolor=' iconColor=ColorArrayList) |
+ ('visibility=' visibility=VisibilityRuleList))*;
Buttongrid:
- {Buttongrid} 'Buttongrid' (('item=' item=ItemRef)? & ('label=' label=(ID | STRING))? &
- (('icon=' icon=Icon) |
- ('icon=[' (IconRules+=IconRule (',' IconRules+=IconRule)*) ']') |
- ('staticIcon=' staticIcon=Icon))? &
- ('buttons=[' buttons+=ButtonDefinition (',' buttons+=ButtonDefinition)* ']')? &
- ('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)*) ']')? &
- ('valuecolor=[' (ValueColor+=ColorArray (',' ValueColor+=ColorArray)*) ']')? &
- ('iconcolor=[' (IconColor+=ColorArray (',' IconColor+=ColorArray)*) ']')? &
- ('visibility=[' (Visibility+=VisibilityRule (',' Visibility+=VisibilityRule)*) ']')?);
+ {Buttongrid} 'Buttongrid' (('item=' item=ItemRef) | ('label=' label=(ID | STRING)) |
+ ('icon=' icon=Icon) | ('icon=' IconRules=IconRuleList) | ('staticIcon=' staticIcon=Icon) |
+ ('buttons=' buttons=ButtonDefinitionList) |
+ ('labelcolor=' labelColor=ColorArrayList) |
+ ('valuecolor=' valueColor=ColorArrayList) |
+ ('iconcolor=' iconColor=ColorArrayList) |
+ ('visibility=' visibility=VisibilityRuleList))*;
Button:
- 'Button' (('item=' item=ItemRef) & ('label=' label=(ID | STRING))? &
- (('icon=' icon=Icon) |
- ('icon=[' (IconRules+=IconRule (',' IconRules+=IconRule)*) ']') |
- ('staticIcon=' staticIcon=Icon))? &
- ('row=' row=INT) & ('column=' column=INT) & (stateless?='stateless')? &
- ('click=' cmd=Command) & ('release=' releaseCmd=Command)? &
- ('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)*) ']')? &
- ('valuecolor=[' (ValueColor+=ColorArray (',' ValueColor+=ColorArray)*) ']')? &
- ('iconcolor=[' (IconColor+=ColorArray (',' IconColor+=ColorArray)*) ']')? &
- ('visibility=[' (Visibility+=VisibilityRule (',' Visibility+=VisibilityRule)*) ']')?);
+ 'Button' (('item=' item=ItemRef) | ('label=' label=(ID | STRING)) |
+ ('icon=' icon=Icon) | ('icon=' IconRules=IconRuleList) | ('staticIcon=' staticIcon=Icon) |
+ ('row=' row=INT) | ('column=' column=INT) | (stateless?='stateless') |
+ ('click=' cmd=Command) | ('release=' releaseCmd=Command) |
+ ('labelcolor=' labelColor=ColorArrayList) |
+ ('valuecolor=' valueColor=ColorArrayList) |
+ ('iconcolor=' iconColor=ColorArrayList) |
+ ('visibility=' visibility=VisibilityRuleList))*;
Default:
- 'Default' (('item=' item=ItemRef) & ('label=' label=(ID | STRING))? &
- (('icon=' icon=Icon) |
- ('icon=[' (IconRules+=IconRule (',' IconRules+=IconRule)*) ']') |
- ('staticIcon=' staticIcon=Icon))? &
- ('height=' height=INT)? &
- ('labelcolor=[' (LabelColor+=ColorArray (',' LabelColor+=ColorArray)*) ']')? &
- ('valuecolor=[' (ValueColor+=ColorArray (',' ValueColor+=ColorArray)*) ']')? &
- ('iconcolor=[' (IconColor+=ColorArray (',' IconColor+=ColorArray)*) ']')? &
- ('visibility=[' (Visibility+=VisibilityRule (',' Visibility+=VisibilityRule)*) ']')?);
+ 'Default' (('item=' item=ItemRef) | ('label=' label=(ID | STRING)) |
+ ('icon=' icon=Icon) | ('icon=' IconRules=IconRuleList) | ('staticIcon=' staticIcon=Icon) |
+ ('height=' height=INT) |
+ ('labelcolor=' labelColor=ColorArrayList) |
+ ('valuecolor=' valueColor=ColorArrayList) |
+ ('iconcolor=' iconColor=ColorArrayList) |
+ ('visibility=' visibility=VisibilityRuleList))*;
ButtonDefinition:
row=INT ':' column=INT ':' cmd=Command '=' label=(ID | STRING) ('=' icon=Icon)?;
+MappingList returns MappingList:
+ '[' elements+=Mapping (',' elements+=Mapping)* ']'
+;
+
+ColorArrayList returns ColorArrayList:
+ '[' elements+=ColorArray (',' elements+=ColorArray)* ']'
+;
+
+IconRuleList returns IconRuleList:
+ '[' elements+=IconRule (',' elements+=IconRule)* ']'
+;
+
+VisibilityRuleList returns VisibilityRuleList:
+ '[' elements+=VisibilityRule (',' elements+=VisibilityRule)* ']'
+;
+
+ButtonDefinitionList returns ButtonDefinitionList:
+ '[' elements+=ButtonDefinition (',' elements+=ButtonDefinition)* ']'
+;
+
Mapping:
cmd=Command (':' releaseCmd=Command)? '=' label=(ID | STRING) ('=' icon=Icon)?;
diff --git a/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/SitemapRuntimeModule.xtend b/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/SitemapRuntimeModule.xtend
index ad4be5c67a2..4244f227512 100644
--- a/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/SitemapRuntimeModule.xtend
+++ b/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/SitemapRuntimeModule.xtend
@@ -21,6 +21,10 @@ import org.eclipse.xtext.conversion.IValueConverterService
import org.eclipse.xtext.linking.lazy.LazyURIEncoder
import com.google.inject.Binder
import com.google.inject.name.Names
+import org.openhab.core.model.sitemap.formatting.SitemapFormatter
+import org.eclipse.xtext.formatting.IFormatter
+import org.openhab.core.model.sitemap.formatting.SitemapIndentationInformation
+import org.eclipse.xtext.formatting.IIndentationInformation
/**
* Use this class to register components to be used at runtime / without the Equinox extension registry.
@@ -30,6 +34,15 @@ class SitemapRuntimeModule extends org.openhab.core.model.sitemap.AbstractSitema
return SitemapConverters
}
+ override Class extends IFormatter> bindIFormatter() {
+ return SitemapFormatter
+ }
+
+ override void configure(Binder binder) {
+ super.configure(binder)
+ binder.bind(IIndentationInformation).to(SitemapIndentationInformation)
+ }
+
override void configureUseIndexFragmentsForLazyLinking(Binder binder) {
binder.bind(Boolean.TYPE).annotatedWith(Names.named(LazyURIEncoder.USE_INDEXED_FRAGMENTS_BINDING)).toInstance(
Boolean.FALSE)
diff --git a/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/SitemapStandaloneSetup.xtend b/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/SitemapStandaloneSetup.xtend
index 1a82db8bc4b..c5cac00799c 100644
--- a/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/SitemapStandaloneSetup.xtend
+++ b/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/SitemapStandaloneSetup.xtend
@@ -15,20 +15,25 @@ package org.openhab.core.model.sitemap
import org.eclipse.emf.ecore.EPackage
import org.eclipse.emf.ecore.resource.Resource
import org.eclipse.xtext.resource.IResourceServiceProvider
+import com.google.inject.Injector
/**
* Initialization support for running Xtext languages
* without equinox extension registry
*/
class SitemapStandaloneSetup extends SitemapStandaloneSetupGenerated {
- def static void doSetup() {
- new SitemapStandaloneSetup().createInjectorAndDoEMFRegistration()
+ static Injector injector;
+
+ def static Injector doSetup() {
+ if (injector === null) {
+ injector = new SitemapStandaloneSetup().createInjectorAndDoEMFRegistration();
+ }
+ return injector;
}
-
+
def static void unregister() {
EPackage.Registry.INSTANCE.remove("https://openhab.org/model/Sitemap");
Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().remove("sitemap");
IResourceServiceProvider.Registry.INSTANCE.getExtensionToFactoryMap().remove("sitemap");
}
-
}
diff --git a/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/fileconverter/AbstractSitemapFileGenerator.java b/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/fileconverter/AbstractSitemapFileGenerator.java
new file mode 100644
index 00000000000..187bd48b00a
--- /dev/null
+++ b/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/fileconverter/AbstractSitemapFileGenerator.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2010-2025 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.model.sitemap.fileconverter;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.model.sitemap.sitemap.Sitemap;
+import org.osgi.service.component.annotations.Activate;
+
+/**
+ * {@link AbstractSitemapFileGenerator} is the base class for any {@link Sitemap} file generator.
+ *
+ * @author Mark Herwege - Initial contribution
+ */
+@NonNullByDefault
+public abstract class AbstractSitemapFileGenerator implements SitemapFileGenerator {
+
+ @Activate
+ public AbstractSitemapFileGenerator() {
+ }
+}
diff --git a/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/fileconverter/SitemapFileGenerator.java b/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/fileconverter/SitemapFileGenerator.java
new file mode 100644
index 00000000000..cb7be4430a0
--- /dev/null
+++ b/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/fileconverter/SitemapFileGenerator.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2010-2025 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.model.sitemap.fileconverter;
+
+import java.io.OutputStream;
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.model.sitemap.sitemap.Sitemap;
+
+/**
+ * {@link SitemapFileGenerator} is the interface to implement by any file generator for {@link Sitemap} object.
+ *
+ * @author Mark Herwege - Initial contribution
+ */
+@NonNullByDefault
+public interface SitemapFileGenerator {
+
+ /**
+ * Returns the format of the file.
+ *
+ * @return the file format
+ */
+ String getFileFormatGenerator();
+
+ /**
+ * Generate the file format for a list of sitemaps.
+ *
+ * @param out the output stream to write the generated syntax to
+ * @param sitemaps the sitemaps
+ */
+ void generateFileFormat(OutputStream out, List sitemaps);
+}
diff --git a/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/formatting/SitemapFormatter.xtend b/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/formatting/SitemapFormatter.xtend
index 6dccbc9cfd9..9eb247cce23 100644
--- a/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/formatting/SitemapFormatter.xtend
+++ b/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/formatting/SitemapFormatter.xtend
@@ -17,26 +17,66 @@ package org.openhab.core.model.sitemap.formatting
import org.eclipse.xtext.formatting.impl.AbstractDeclarativeFormatter
import org.eclipse.xtext.formatting.impl.FormattingConfig
-// import com.google.inject.Inject;
-// import org.openhab.core.model.services.SitemapGrammarAccess
+import com.google.inject.Inject
+import org.openhab.core.model.sitemap.services.SitemapGrammarAccess
/**
* This class contains custom formatting description.
- *
- * see : http://www.eclipse.org/Xtext/documentation.html#formatting
- * on how and when to use it
- *
- * Also see {@link org.eclipse.xtext.xtext.XtextFormatter} as an example
*/
class SitemapFormatter extends AbstractDeclarativeFormatter {
-// @Inject extension SitemapGrammarAccess
-
- override protected void configureFormatting(FormattingConfig c) {
-// It's usually a good idea to activate the following three statements.
-// They will add and preserve newlines around comments
-// c.setLinewrap(0, 1, 2).before(SL_COMMENTRule)
-// c.setLinewrap(0, 1, 2).before(ML_COMMENTRule)
-// c.setLinewrap(0, 1, 1).after(ML_COMMENTRule)
+ @Inject extension SitemapGrammarAccess
+
+ override protected void configureFormatting(FormattingConfig c) {
+ c.wrappedLineIndentation = 4
+ c.autoLinewrap = 200
+
+ c.setLinewrap(1, 1, 2).before(widgetRule)
+
+ c.setIndentationIncrement.after("{")
+ c.setLinewrap().before("}")
+ c.setIndentationDecrement.before("}")
+ c.setLinewrap().after("}")
+
+ c.setNoSpace().withinKeywordPairs("<", ">")
+ c.setNoSpace().withinKeywordPairs("(", ")")
+ c.setNoSpace().withinKeywordPairs("[", "]")
+
+ c.setNoSpace().after("item=", "label=", "icon=", "staticIcon=")
+ c.setNoSpace().after("url=", "refresh=", "encoding=", "service=", "period=", "legend=", "forceasitem=", "yAxisDecimalPattern=", "interpolation=", "height=")
+ c.setNoSpace().after("minValue=", "maxValue=", "step=", "inputHint=", "row=", "column=", "click=", "release=")
+ c.setNoSpace().after("labelcolor=", "valuecolor=", "iconcolor=", "visibility=", "mappings=", "buttons=")
+
+ c.setNoSpace().before(",")
+ c.setNoSpace().around(":", "=")
+
+ c.setLinewrap(0, 1, 2).before(SL_COMMENTRule)
+ c.setLinewrap(0, 1, 2).before(ML_COMMENTRule)
+ c.setLinewrap(0, 1, 1).after(ML_COMMENTRule)
}
+
+ def withinKeywordPairs(FormattingConfig.NoSpaceLocator locator, String leftKW, String rightKW) {
+ for (pair : findKeywordPairs(leftKW, rightKW)) {
+ locator.after(pair.first)
+ locator.before(pair.second)
+ }
+ }
+
+ def around(FormattingConfig.ElementLocator locator, String ... listKW) {
+ for (keyword : findKeywords(listKW)) {
+ locator.around(keyword)
+ }
+ }
+
+ def after(FormattingConfig.ElementLocator locator, String ... listKW) {
+ for (keyword : findKeywords(listKW)) {
+ locator.after(keyword)
+ }
+ }
+
+ def before(FormattingConfig.ElementLocator locator, String ... listKW) {
+ for (keyword : findKeywords(listKW)) {
+ locator.before(keyword)
+ }
+ }
}
diff --git a/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/formatting/SitemapIndentationInformation.java b/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/formatting/SitemapIndentationInformation.java
new file mode 100644
index 00000000000..c969d4af325
--- /dev/null
+++ b/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/formatting/SitemapIndentationInformation.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2010-2025 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.model.sitemap.formatting;
+
+import org.eclipse.xtext.formatting.IIndentationInformation;
+
+public class SitemapIndentationInformation implements IIndentationInformation {
+ @Override
+ public String getIndentString() {
+ return " "; // 2 spaces
+ }
+}
diff --git a/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/internal/fileconverter/DslSitemapFileConverter.java b/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/internal/fileconverter/DslSitemapFileConverter.java
new file mode 100644
index 00000000000..336b56891b7
--- /dev/null
+++ b/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/internal/fileconverter/DslSitemapFileConverter.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2010-2025 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.model.sitemap.internal.fileconverter;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.xtext.serializer.ISerializer;
+import org.openhab.core.model.core.ModelRepository;
+import org.openhab.core.model.sitemap.SitemapStandaloneSetup;
+import org.openhab.core.model.sitemap.fileconverter.AbstractSitemapFileGenerator;
+import org.openhab.core.model.sitemap.fileconverter.SitemapFileGenerator;
+import org.openhab.core.model.sitemap.sitemap.Sitemap;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * {@link DslSitemapFileConverter} is the DSL file converter for {@link Sitemap} object
+ * with the capabilities of parsing and generating file.
+ *
+ * @author Mark Herwege - Initial contribution
+ */
+@NonNullByDefault
+@Component(immediate = true, service = SitemapFileGenerator.class)
+public class DslSitemapFileConverter extends AbstractSitemapFileGenerator {
+
+ private final Logger logger = LoggerFactory.getLogger(DslSitemapFileConverter.class);
+
+ private final ModelRepository modelRepository;
+
+ private final ISerializer serializer;
+
+ @Activate
+ public DslSitemapFileConverter(final @Reference ModelRepository modelRepository) {
+ this.modelRepository = modelRepository;
+ this.serializer = SitemapStandaloneSetup.doSetup().getInstance(ISerializer.class);
+ }
+
+ @Override
+ public String getFileFormatGenerator() {
+ return "DSL";
+ }
+
+ @Override
+ public synchronized void generateFileFormat(OutputStream out, List sitemaps) {
+ if (sitemaps.isEmpty()) {
+ return;
+ }
+ for (Sitemap sitemap : sitemaps) {
+ modelRepository.generateSyntaxFromModel(out, "sitemap", sitemap, serializer);
+ try {
+ out.write(System.lineSeparator().getBytes());
+ } catch (IOException e) {
+ logger.warn("Exception when saving the sitemap {}", sitemap.getName());
+ }
+ }
+ }
+}
diff --git a/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/validation/SitemapValidator.xtend b/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/validation/SitemapValidator.xtend
index 3749f783bc6..1f6a5a6cde5 100644
--- a/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/validation/SitemapValidator.xtend
+++ b/bundles/org.openhab.core.model.sitemap/src/org/openhab/core/model/sitemap/validation/SitemapValidator.xtend
@@ -29,6 +29,11 @@ import java.math.BigDecimal
import org.openhab.core.model.sitemap.sitemap.Input
import org.eclipse.xtext.nodemodel.util.NodeModelUtils
import org.openhab.core.model.sitemap.sitemap.Chart
+import org.openhab.core.model.sitemap.sitemap.Webview
+import org.openhab.core.model.sitemap.sitemap.Text
+import org.openhab.core.model.sitemap.sitemap.Image
+import org.openhab.core.model.sitemap.sitemap.Video
+import org.openhab.core.model.sitemap.sitemap.Slider
//import org.eclipse.xtext.validation.Check
/**
@@ -40,17 +45,45 @@ class SitemapValidator extends AbstractSitemapValidator {
val ALLOWED_HINTS = #["text", "number", "date", "time", "datetime"]
val ALLOWED_INTERPOLATION = #["linear", "step"]
+
+ @Check
+ def void checkWidgetHasItem(Widget w) {
+ if (!(w instanceof Frame || w instanceof Text || w instanceof Image || w instanceof Video || w instanceof Webview || w instanceof Buttongrid) && w.item === null) {
+ val node = NodeModelUtils.getNode(w)
+ val line = node.getStartLine()
+ error("'" + w.getClass().getSimpleName() + "' widget doesn't have item defined at line " + line,
+ SitemapPackage.Literals.INPUT.getEStructuralFeature(SitemapPackage.WIDGET))
+ }
+ }
+
+ @Check
+ def void checkWidgetIcon(Widget w) {
+ if ((w.icon !== null || w.iconRules !== null) && w.staticIcon !== null) {
+ val node = NodeModelUtils.getNode(w)
+ val line = node.getStartLine()
+ error("Widget '" + w.getClass().getSimpleName() + "' has icon '" + w.icon + "' and staticIcon '" + w.staticIcon + "' defined at the same time " + line,
+ SitemapPackage.Literals.INPUT.getEStructuralFeature(SitemapPackage.WIDGET))
+ }
+ if (w.icon !== null && w.iconRules !== null) {
+ val node = NodeModelUtils.getNode(w)
+ val line = node.getStartLine()
+ error("Widget '" + w.getClass().getSimpleName() + "' has icon '" + w.icon + "' and icon rules '" + w.iconRules + "' defined at the same time " + line,
+ SitemapPackage.Literals.INPUT.getEStructuralFeature(SitemapPackage.WIDGET))
+ }
+ }
@Check
def void checkFramesInFrame(Frame frame) {
for (Widget w : frame.children) {
+ val node = NodeModelUtils.getNode(w)
+ val line = node.getStartLine()
if (w instanceof Frame) {
- error("Frames must not contain other frames",
+ error("Frames must not contain other frames at line " + line,
SitemapPackage.Literals.FRAME.getEStructuralFeature(SitemapPackage.FRAME__CHILDREN));
return;
}
if (w instanceof Button) {
- error("Frames should not contain Button, Button is allowed only in Buttongrid",
+ error("Frames should not contain Button, Button is allowed only in Buttongrid at line " + line,
SitemapPackage.Literals.FRAME.getEStructuralFeature(SitemapPackage.FRAME__CHILDREN));
return;
}
@@ -63,8 +96,10 @@ class SitemapValidator extends AbstractSitemapValidator {
var containsOtherWidgets = false
for (Widget w : sitemap.children) {
+ val node = NodeModelUtils.getNode(w)
+ val line = node.getStartLine()
if (w instanceof Button) {
- error("Sitemap should not contain Button, Button is allowed only in Buttongrid",
+ error("Sitemap should not contain Button, Button is allowed only in Buttongrid at line " + line,
SitemapPackage.Literals.SITEMAP.getEStructuralFeature(SitemapPackage.SITEMAP__NAME));
return;
}
@@ -74,7 +109,7 @@ class SitemapValidator extends AbstractSitemapValidator {
containsOtherWidgets = true
}
if (containsFrames && containsOtherWidgets) {
- error("Sitemap should contain either only frames or none at all",
+ error("Sitemap should contain either only frames or none at all at line " + line,
SitemapPackage.Literals.SITEMAP.getEStructuralFeature(SitemapPackage.SITEMAP__NAME));
return
}
@@ -94,8 +129,10 @@ class SitemapValidator extends AbstractSitemapValidator {
var containsFrames = false
var containsOtherWidgets = false
for (Widget w : widget.children) {
+ val node = NodeModelUtils.getNode(w)
+ val line = node.getStartLine()
if (w instanceof Button) {
- error("Linkable widget should not contain Button, Button is allowed only in Buttongrid",
+ error("Linkable widget should not contain Button, Button is allowed only in Buttongrid at line " + line,
SitemapPackage.Literals.FRAME.getEStructuralFeature(SitemapPackage.LINKABLE_WIDGET__CHILDREN));
return;
}
@@ -105,7 +142,7 @@ class SitemapValidator extends AbstractSitemapValidator {
containsOtherWidgets = true
}
if (containsFrames && containsOtherWidgets) {
- error("Linkable widget should contain either only frames or none at all",
+ error("Linkable widget should contain either only frames or none at all at line " + line,
SitemapPackage.Literals.FRAME.getEStructuralFeature(SitemapPackage.LINKABLE_WIDGET__CHILDREN));
return
}
@@ -114,14 +151,18 @@ class SitemapValidator extends AbstractSitemapValidator {
@Check
def void checkWidgetsInButtongrid(Buttongrid grid) {
- val nb = grid.getButtons.size()
+ val nb = grid.getButtons !== null ? grid.getButtons.getElements.size() : 0
if (nb > 0 && grid.item === null) {
- error("To use the \"buttons\" parameter in a Buttongrid, the \"item\" parameter is required",
+ val node = NodeModelUtils.getNode(grid)
+ val line = node.getStartLine()
+ error("To use the \"buttons\" parameter in a Buttongrid, the \"item\" parameter is required at line " + line,
SitemapPackage.Literals.BUTTONGRID.getEStructuralFeature(SitemapPackage.BUTTONGRID__ITEM));
}
for (Widget w : grid.children) {
if (!(w instanceof Button)) {
- error("Buttongrid must contain only Button",
+ val node = NodeModelUtils.getNode(w)
+ val line = node.getStartLine()
+ error("Buttongrid must contain only Button at line " + line,
SitemapPackage.Literals.BUTTONGRID.getEStructuralFeature(SitemapPackage.BUTTONGRID__CHILDREN));
return;
}
@@ -129,48 +170,82 @@ class SitemapValidator extends AbstractSitemapValidator {
}
@Check
- def void checkSetpoints(Setpoint sp) {
+ def void checkSetpointParameters(Setpoint sp) {
+ val node = NodeModelUtils.getNode(sp)
+ val line = node.getStartLine()
if (BigDecimal.ZERO == sp.step) {
- error("Setpoint on item '" + sp.item + "' has step size of 0",
+ error("Setpoint widget has step size of '0' at line " + line,
SitemapPackage.Literals.SETPOINT.getEStructuralFeature(SitemapPackage.SETPOINT__STEP));
}
-
if (sp.step !== null && sp.step < BigDecimal.ZERO) {
- error("Setpoint on item '" + sp.item + "' has negative step size",
+ error("Setpoint has negative step size of '" + sp.step + "' at line " + line,
SitemapPackage.Literals.SETPOINT.getEStructuralFeature(SitemapPackage.SETPOINT__STEP));
}
-
if (sp.minValue !== null && sp.maxValue !== null && sp.minValue > sp.maxValue) {
- error("Setpoint on item '" + sp.item + "' has larger minValue than maxValue",
+ error("Setpoint on item has larger minValue '" + sp.minValue + "' than maxValue '" + sp.maxValue + "' at line " + line,
SitemapPackage.Literals.SETPOINT.getEStructuralFeature(SitemapPackage.SETPOINT__MIN_VALUE));
}
}
@Check
- def void checkColortemperaturepicker(Colortemperaturepicker ctp) {
+ def void checkSliderParameters(Slider s) {
+ val node = NodeModelUtils.getNode(s)
+ val line = node.getStartLine()
+ if (BigDecimal.ZERO == s.step) {
+ error("Slider widget has step size of '0' at line " + line,
+ SitemapPackage.Literals.SLIDER.getEStructuralFeature(SitemapPackage.SLIDER__STEP));
+ }
+ if (s.step !== null && s.step < BigDecimal.ZERO) {
+ error("Slider has negative step size of '" + s.step + "' at line " + line,
+ SitemapPackage.Literals.SLIDER.getEStructuralFeature(SitemapPackage.SLIDER__STEP));
+ }
+ if (s.minValue !== null && s.maxValue !== null && s.minValue > s.maxValue) {
+ error("Slider on item has larger minValue '" + s.minValue + "' than maxValue '" + s.maxValue + "' at line " + line,
+ SitemapPackage.Literals.SLIDER.getEStructuralFeature(SitemapPackage.SLIDER__MIN_VALUE));
+ }
+ }
+
+ @Check
+ def void checkColortemperaturepickerParameters(Colortemperaturepicker ctp) {
if (ctp.minValue !== null && ctp.maxValue !== null && ctp.minValue > ctp.maxValue) {
- error("Colortemperaturepicker on item '" + ctp.item + "' has larger minValue than maxValue",
+ val node = NodeModelUtils.getNode(ctp)
+ val line = node.getStartLine()
+ error("Colortemperaturepicker widget has larger minValue '" + ctp.minValue + "' than maxValue '" + ctp.maxValue + "' at line " + line,
SitemapPackage.Literals.COLORTEMPERATUREPICKER.getEStructuralFeature(SitemapPackage.COLORTEMPERATUREPICKER__MIN_VALUE));
}
}
@Check
- def void checkInputHintParameter(Input i) {
+ def void checkInputParameters(Input i) {
if (i.inputHint !== null && !ALLOWED_HINTS.contains(i.inputHint)) {
val node = NodeModelUtils.getNode(i)
val line = node.getStartLine()
- error("Input on item '" + i.item + "' has invalid inputHint '" + i.inputHint + "' at line " + line,
+ error("Input widget has invalid inputHint '" + i.inputHint + "' at line " + line,
SitemapPackage.Literals.INPUT.getEStructuralFeature(SitemapPackage.INPUT__INPUT_HINT))
}
}
@Check
- def void checkInterpolationParameter(Chart i) {
- if (i.interpolation !== null && !ALLOWED_INTERPOLATION.contains(i.interpolation)) {
- val node = NodeModelUtils.getNode(i)
- val line = node.getStartLine()
- error("Input on item '" + i.item + "' has invalid interpolation '" + i.interpolation + "' at line " + line,
+ def void checkChartParameters(Chart c) {
+ val node = NodeModelUtils.getNode(c)
+ val line = node.getStartLine()
+ if (c.interpolation !== null && !ALLOWED_INTERPOLATION.contains(c.interpolation)) {
+ error("Chart widget has invalid interpolation '" + c.interpolation + "' at line " + line,
SitemapPackage.Literals.INPUT.getEStructuralFeature(SitemapPackage.CHART__INTERPOLATION))
}
+ if (c.period === null) {
+ error("Chart widget doesn't have period defined at line " + line,
+ SitemapPackage.Literals.INPUT.getEStructuralFeature(SitemapPackage.CHART__PERIOD))
+ }
+ }
+
+ @Check
+ def void checkVideoParameters(Video v) {
+ if (v.url === null) {
+ val node = NodeModelUtils.getNode(v)
+ val line = node.getStartLine()
+ error("Video widget doesn't have url defined at line " + line,
+ SitemapPackage.Literals.INPUT.getEStructuralFeature(SitemapPackage.VIDEO__URL))
+ }
}
}
diff --git a/bundles/org.openhab.core.model.thing/src/org/openhab/core/model/thing/ThingStandaloneSetup.xtend b/bundles/org.openhab.core.model.thing/src/org/openhab/core/model/thing/ThingStandaloneSetup.xtend
index 3514a130a6e..376eb9482cb 100644
--- a/bundles/org.openhab.core.model.thing/src/org/openhab/core/model/thing/ThingStandaloneSetup.xtend
+++ b/bundles/org.openhab.core.model.thing/src/org/openhab/core/model/thing/ThingStandaloneSetup.xtend
@@ -15,20 +15,25 @@ package org.openhab.core.model.thing
import org.eclipse.emf.ecore.EPackage
import org.eclipse.emf.ecore.resource.Resource
import org.eclipse.xtext.resource.IResourceServiceProvider
+import com.google.inject.Injector
/**
* Initialization support for running Xtext languages
* without equinox extension registry
*/
class ThingStandaloneSetup extends ThingStandaloneSetupGenerated {
- def static void doSetup() {
- new ThingStandaloneSetup().createInjectorAndDoEMFRegistration()
+ static Injector injector;
+
+ def static Injector doSetup() {
+ if (injector === null) {
+ injector = new ThingStandaloneSetup().createInjectorAndDoEMFRegistration();
+ }
+ return injector;
}
-
+
def static void unregister() {
EPackage.Registry.INSTANCE.remove("https://openhab.org/model/Thing");
Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().remove("things");
IResourceServiceProvider.Registry.INSTANCE.getExtensionToFactoryMap().remove("things");
}
-
}
diff --git a/bundles/org.openhab.core.model.thing/src/org/openhab/core/model/thing/internal/fileconverter/DslThingFileConverter.java b/bundles/org.openhab.core.model.thing/src/org/openhab/core/model/thing/internal/fileconverter/DslThingFileConverter.java
index 728b8cc801a..14868cde1ac 100644
--- a/bundles/org.openhab.core.model.thing/src/org/openhab/core/model/thing/internal/fileconverter/DslThingFileConverter.java
+++ b/bundles/org.openhab.core.model.thing/src/org/openhab/core/model/thing/internal/fileconverter/DslThingFileConverter.java
@@ -22,8 +22,10 @@
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.xtext.serializer.ISerializer;
import org.openhab.core.config.core.ConfigDescriptionRegistry;
import org.openhab.core.model.core.ModelRepository;
+import org.openhab.core.model.thing.ThingStandaloneSetup;
import org.openhab.core.model.thing.thing.ModelBridge;
import org.openhab.core.model.thing.thing.ModelChannel;
import org.openhab.core.model.thing.thing.ModelProperty;
@@ -60,6 +62,8 @@ public class DslThingFileConverter extends AbstractThingFileGenerator {
private final ModelRepository modelRepository;
+ private final ISerializer serializer;
+
@Activate
public DslThingFileConverter(final @Reference ModelRepository modelRepository,
final @Reference ThingTypeRegistry thingTypeRegistry,
@@ -67,6 +71,7 @@ public DslThingFileConverter(final @Reference ModelRepository modelRepository,
final @Reference ConfigDescriptionRegistry configDescRegistry) {
super(thingTypeRegistry, channelTypeRegistry, configDescRegistry);
this.modelRepository = modelRepository;
+ this.serializer = ThingStandaloneSetup.doSetup().getInstance(ISerializer.class);
}
@Override
@@ -91,7 +96,7 @@ public synchronized void generateFileFormat(OutputStream out, List things
// Double quotes are unexpectedly generated in thing UID when the segment contains a -.
// Fix that by removing these double quotes. Requires to first build the generated syntax as a String
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
- modelRepository.generateSyntaxFromModel(outputStream, "things", model);
+ modelRepository.generateSyntaxFromModel(outputStream, "things", model, serializer);
String syntax = new String(outputStream.toByteArray()).replaceAll(":\"([a-zA-Z0-9_][a-zA-Z0-9_-]*)\"", ":$1");
try {
out.write(syntax.getBytes());
diff --git a/bundles/org.openhab.core.model.yaml/pom.xml b/bundles/org.openhab.core.model.yaml/pom.xml
index e4f130069ae..f3778f68c52 100644
--- a/bundles/org.openhab.core.model.yaml/pom.xml
+++ b/bundles/org.openhab.core.model.yaml/pom.xml
@@ -35,5 +35,15 @@
org.openhab.core.thing
${project.version}
+
+ org.openhab.core.bundles
+ org.openhab.core.model.sitemap
+ ${project.version}
+
+
+ org.openhab.core.bom
+ org.openhab.core.bom.compile-model
+ pom
+
diff --git a/bundles/org.openhab.core.model.yaml/src/main/java/org/openhab/core/model/yaml/internal/sitemaps/YamlMappingDTO.java b/bundles/org.openhab.core.model.yaml/src/main/java/org/openhab/core/model/yaml/internal/sitemaps/YamlMappingDTO.java
new file mode 100644
index 00000000000..ec386f868c6
--- /dev/null
+++ b/bundles/org.openhab.core.model.yaml/src/main/java/org/openhab/core/model/yaml/internal/sitemaps/YamlMappingDTO.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2010-2025 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.model.yaml.internal.sitemaps;
+
+import java.util.List;
+import java.util.Objects;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.core.model.yaml.YamlElement;
+import org.openhab.core.model.yaml.YamlElementName;
+
+/**
+ * The {@link YamlMappingDTO} is a data transfer object used to serialize a sitemap in a YAML configuration file.
+ *
+ * @author Mark Herwege - Initial contribution
+ */
+@YamlElementName("mapping")
+public class YamlMappingDTO implements YamlElement, Cloneable {
+
+ public String uid;
+ public String cmd;
+ public String releaseCmd;
+ public String label;
+ public String icon;
+
+ public YamlMappingDTO() {
+ }
+
+ @Override
+ public @NonNull String getId() {
+ return uid == null ? "" : uid;
+ }
+
+ @Override
+ public void setId(@NonNull String id) {
+ uid = id;
+ }
+
+ @Override
+ public YamlElement cloneWithoutId() {
+ YamlMappingDTO copy;
+ try {
+ copy = (YamlMappingDTO) super.clone();
+ copy.uid = null;
+ return copy;
+ } catch (CloneNotSupportedException e) {
+ // Will never happen
+ return new YamlMappingDTO();
+ }
+ }
+
+ @Override
+ public boolean isValid(@Nullable List<@NonNull String> errors, @Nullable List<@NonNull String> warnings) {
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(uid, cmd, label);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (this == obj) {
+ return true;
+ } else if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ YamlMappingDTO other = (YamlMappingDTO) obj;
+ return Objects.equals(uid, other.uid) && Objects.equals(label, other.label) && Objects.equals(cmd, other.cmd);
+ }
+}
diff --git a/bundles/org.openhab.core.model.yaml/src/main/java/org/openhab/core/model/yaml/internal/sitemaps/YamlRuleDTO.java b/bundles/org.openhab.core.model.yaml/src/main/java/org/openhab/core/model/yaml/internal/sitemaps/YamlRuleDTO.java
new file mode 100644
index 00000000000..043a8d017c6
--- /dev/null
+++ b/bundles/org.openhab.core.model.yaml/src/main/java/org/openhab/core/model/yaml/internal/sitemaps/YamlRuleDTO.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2010-2025 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.model.yaml.internal.sitemaps;
+
+import java.util.List;
+import java.util.Objects;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.core.model.yaml.YamlElement;
+import org.openhab.core.model.yaml.YamlElementName;
+
+/**
+ * The {@link YamlRuleDTO} is a data transfer object used to serialize a sitemap widget rule in a YAML configuration
+ * file.
+ *
+ * @author Mark Herwege - Initial contribution
+ */
+@YamlElementName("rule")
+public class YamlRuleDTO implements YamlElement, Cloneable {
+
+ public String uid;
+ public List conditions;
+ public String argument;
+
+ public YamlRuleDTO() {
+ }
+
+ @Override
+ public @NonNull String getId() {
+ return uid == null ? "" : uid;
+ }
+
+ @Override
+ public void setId(@NonNull String id) {
+ uid = id;
+ }
+
+ @Override
+ public YamlElement cloneWithoutId() {
+ YamlRuleDTO copy;
+ try {
+ copy = (YamlRuleDTO) super.clone();
+ copy.uid = null;
+ return copy;
+ } catch (CloneNotSupportedException e) {
+ // Will never happen
+ return new YamlRuleDTO();
+ }
+ }
+
+ @Override
+ public boolean isValid(@Nullable List<@NonNull String> errors, @Nullable List<@NonNull String> warnings) {
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(uid, conditions);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (this == obj) {
+ return true;
+ } else if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ YamlRuleDTO other = (YamlRuleDTO) obj;
+ return Objects.equals(uid, other.uid) && Objects.equals(conditions, other.conditions);
+ }
+
+ public class Condition {
+ public String item;
+ public String condition;
+ public String value;
+ }
+}
diff --git a/bundles/org.openhab.core.model.yaml/src/main/java/org/openhab/core/model/yaml/internal/sitemaps/YamlSitemapDTO.java b/bundles/org.openhab.core.model.yaml/src/main/java/org/openhab/core/model/yaml/internal/sitemaps/YamlSitemapDTO.java
new file mode 100644
index 00000000000..a627b29feab
--- /dev/null
+++ b/bundles/org.openhab.core.model.yaml/src/main/java/org/openhab/core/model/yaml/internal/sitemaps/YamlSitemapDTO.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2010-2025 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.model.yaml.internal.sitemaps;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.core.model.yaml.YamlElement;
+import org.openhab.core.model.yaml.YamlElementName;
+
+/**
+ * The {@link YamlSitemapDTO} is a data transfer object used to serialize a sitemap in a YAML configuration file.
+ *
+ * @author Mark Herwege - Initial contribution
+ */
+@YamlElementName("sitemap")
+public class YamlSitemapDTO implements YamlElement, Cloneable {
+
+ public String uid;
+ public String label;
+ public String icon;
+ public List> widgets;
+
+ public YamlSitemapDTO() {
+ }
+
+ @Override
+ public @NonNull String getId() {
+ return uid == null ? "" : uid;
+ }
+
+ @Override
+ public void setId(@NonNull String id) {
+ uid = id;
+ }
+
+ @Override
+ public YamlElement cloneWithoutId() {
+ YamlSitemapDTO copy;
+ try {
+ copy = (YamlSitemapDTO) super.clone();
+ copy.uid = null;
+ return copy;
+ } catch (CloneNotSupportedException e) {
+ // Will never happen
+ return new YamlSitemapDTO();
+ }
+ }
+
+ @Override
+ public boolean isValid(@Nullable List<@NonNull String> errors, @Nullable List<@NonNull String> warnings) {
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(uid, label);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (this == obj) {
+ return true;
+ } else if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ YamlSitemapDTO other = (YamlSitemapDTO) obj;
+ return Objects.equals(uid, other.uid);
+ }
+}
diff --git a/bundles/org.openhab.core.model.yaml/src/main/java/org/openhab/core/model/yaml/internal/sitemaps/YamlWidgetDTO.java b/bundles/org.openhab.core.model.yaml/src/main/java/org/openhab/core/model/yaml/internal/sitemaps/YamlWidgetDTO.java
new file mode 100644
index 00000000000..bdab5da763a
--- /dev/null
+++ b/bundles/org.openhab.core.model.yaml/src/main/java/org/openhab/core/model/yaml/internal/sitemaps/YamlWidgetDTO.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2010-2025 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.model.yaml.internal.sitemaps;
+
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.core.model.yaml.YamlElement;
+import org.openhab.core.model.yaml.YamlElementName;
+
+/**
+ * The {@link YamlWidgetDTO} is a data transfer object used to serialize a sitemap widget in a YAML configuration file.
+ *
+ * @author Mark Herwege - Initial contribution
+ */
+@YamlElementName("widget")
+public class YamlWidgetDTO implements YamlElement, Cloneable {
+
+ public String item;
+ public String label;
+ public String icon;
+ public List iconRules;
+ public Boolean staticIcon;
+
+ public String url;
+ public Integer refresh;
+ public String encoding;
+ public String service;
+ public String period;
+ public Boolean legend;
+ public Boolean forceAsItem;
+ public String yAxisDecimalPattern;
+ public String interpolation;
+ public Integer height;
+ public Boolean switchEnabled;
+ public Boolean releaseOnly;
+ public BigDecimal minValue;
+ public BigDecimal maxValue;
+ public BigDecimal step;
+ public String inputHint;
+ public Integer row;
+ public Integer column;
+ public Boolean stateless;
+ public String cmd;
+ public String releaseCmd;
+
+ public List mappings;
+
+ public List labelColor;
+ public List valueColor;
+ public List iconColor;
+ public List visibility;
+
+ public List> widgets;
+
+ public YamlWidgetDTO() {
+ }
+
+ @Override
+ public @NonNull String getId() {
+ return "";
+ }
+
+ @Override
+ public void setId(@NonNull String id) {
+ }
+
+ @Override
+ public YamlElement cloneWithoutId() {
+ YamlWidgetDTO copy;
+ try {
+ copy = (YamlWidgetDTO) super.clone();
+ return copy;
+ } catch (CloneNotSupportedException e) {
+ // Will never happen
+ return new YamlWidgetDTO();
+ }
+ }
+
+ @Override
+ public boolean isValid(@Nullable List<@NonNull String> errors, @Nullable List<@NonNull String> warnings) {
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(item, label, widgets);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (this == obj) {
+ return true;
+ } else if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ YamlWidgetDTO other = (YamlWidgetDTO) obj;
+ return Objects.equals(item, other.item) && Objects.equals(label, other.label)
+ && Objects.equals(widgets, other.widgets);
+ }
+}
diff --git a/bundles/org.openhab.core.model.yaml/src/main/java/org/openhab/core/model/yaml/internal/sitemaps/fileconverter/YamlSitemapFileConverter.java b/bundles/org.openhab.core.model.yaml/src/main/java/org/openhab/core/model/yaml/internal/sitemaps/fileconverter/YamlSitemapFileConverter.java
new file mode 100644
index 00000000000..91b08ba559b
--- /dev/null
+++ b/bundles/org.openhab.core.model.yaml/src/main/java/org/openhab/core/model/yaml/internal/sitemaps/fileconverter/YamlSitemapFileConverter.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright (c) 2010-2025 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.core.model.yaml.internal.sitemaps.fileconverter;
+
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import org.eclipse.emf.common.util.EList;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.core.model.sitemap.fileconverter.AbstractSitemapFileGenerator;
+import org.openhab.core.model.sitemap.fileconverter.SitemapFileGenerator;
+import org.openhab.core.model.sitemap.sitemap.ButtonDefinitionList;
+import org.openhab.core.model.sitemap.sitemap.Buttongrid;
+import org.openhab.core.model.sitemap.sitemap.ColorArray;
+import org.openhab.core.model.sitemap.sitemap.ColorArrayList;
+import org.openhab.core.model.sitemap.sitemap.Condition;
+import org.openhab.core.model.sitemap.sitemap.IconRule;
+import org.openhab.core.model.sitemap.sitemap.IconRuleList;
+import org.openhab.core.model.sitemap.sitemap.LinkableWidget;
+import org.openhab.core.model.sitemap.sitemap.Mapping;
+import org.openhab.core.model.sitemap.sitemap.MappingList;
+import org.openhab.core.model.sitemap.sitemap.Sitemap;
+import org.openhab.core.model.sitemap.sitemap.SitemapFactory;
+import org.openhab.core.model.sitemap.sitemap.SitemapPackage;
+import org.openhab.core.model.sitemap.sitemap.VisibilityRule;
+import org.openhab.core.model.sitemap.sitemap.VisibilityRuleList;
+import org.openhab.core.model.sitemap.sitemap.Widget;
+import org.openhab.core.model.sitemap.sitemap.impl.ButtonImpl;
+import org.openhab.core.model.sitemap.sitemap.impl.ButtongridImpl;
+import org.openhab.core.model.sitemap.sitemap.impl.ChartImpl;
+import org.openhab.core.model.sitemap.sitemap.impl.ColortemperaturepickerImpl;
+import org.openhab.core.model.sitemap.sitemap.impl.DefaultImpl;
+import org.openhab.core.model.sitemap.sitemap.impl.ImageImpl;
+import org.openhab.core.model.sitemap.sitemap.impl.InputImpl;
+import org.openhab.core.model.sitemap.sitemap.impl.MapviewImpl;
+import org.openhab.core.model.sitemap.sitemap.impl.SelectionImpl;
+import org.openhab.core.model.sitemap.sitemap.impl.SetpointImpl;
+import org.openhab.core.model.sitemap.sitemap.impl.SliderImpl;
+import org.openhab.core.model.sitemap.sitemap.impl.SwitchImpl;
+import org.openhab.core.model.sitemap.sitemap.impl.VideoImpl;
+import org.openhab.core.model.sitemap.sitemap.impl.WebviewImpl;
+import org.openhab.core.model.yaml.YamlElement;
+import org.openhab.core.model.yaml.YamlModelRepository;
+import org.openhab.core.model.yaml.internal.sitemaps.YamlMappingDTO;
+import org.openhab.core.model.yaml.internal.sitemaps.YamlRuleDTO;
+import org.openhab.core.model.yaml.internal.sitemaps.YamlSitemapDTO;
+import org.openhab.core.model.yaml.internal.sitemaps.YamlWidgetDTO;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+
+/**
+ * {@link YamlSitemapFileConverter} is the YAML file converter for {@link Sitemap} objects.
+ *
+ * @author Mark Herwege - Initial contribution
+ */
+@NonNullByDefault
+@Component(immediate = true, service = SitemapFileGenerator.class)
+public class YamlSitemapFileConverter extends AbstractSitemapFileGenerator {
+
+ private final YamlModelRepository modelRepository;
+
+ @Activate
+ public YamlSitemapFileConverter(final @Reference YamlModelRepository modelRepository) {
+ super();
+ this.modelRepository = modelRepository;
+ }
+
+ @Override
+ public String getFileFormatGenerator() {
+ return "YAML";
+ }
+
+ @Override
+ public synchronized void generateFileFormat(OutputStream out, List sitemaps) {
+ List elements = new ArrayList<>();
+ sitemaps.forEach(sitemap -> {
+ elements.add(buildSitemapDTO(sitemap));
+ });
+ modelRepository.generateSyntaxFromElements(out, elements);
+ }
+
+ private YamlSitemapDTO buildSitemapDTO(Sitemap sitemap) {
+ YamlSitemapDTO dto = new YamlSitemapDTO();
+ dto.uid = sitemap.getName();
+ dto.label = sitemap.getLabel();
+ dto.icon = sitemap.getIcon();
+ EList children = sitemap.getChildren();
+ if (children != null && !children.isEmpty()) {
+ dto.widgets = children.stream().map(w -> buildWidgetDTO(w)).toList();
+ }
+ return dto;
+ }
+
+ private Map.Entry buildWidgetDTO(Widget widget) {
+ YamlWidgetDTO dto = new YamlWidgetDTO();
+ if (!(widget instanceof ButtongridImpl)) {
+ // We will convert Buttongrid with buttons definition to explicit Button widget, so don't set item
+ dto.item = widget.getItem();
+ }
+ dto.label = widget.getLabel();
+
+ dto.icon = widget.getStaticIcon() != null ? widget.getStaticIcon()
+ : (widget.getIconRules() == null ? widget.getIcon() : null);
+ IconRuleList iconRuleList = widget.getIconRules();
+ if (iconRuleList != null && iconRuleList.getElements() != null && !iconRuleList.getElements().isEmpty()) {
+ dto.iconRules = iconRuleList.getElements().stream().map(e -> buildRuleDTO(e)).toList();
+ }
+ dto.staticIcon = widget.getStaticIcon() != null ? true : null;
+
+ switch (widget) {
+ case ImageImpl imageWidget: {
+ dto.url = imageWidget.getUrl();
+ dto.refresh = imageWidget.getRefresh();
+ break;
+ }
+ case VideoImpl videoWidget: {
+ dto.url = videoWidget.getUrl();
+ dto.encoding = videoWidget.getEncoding();
+ break;
+ }
+ case ChartImpl chartWidget: {
+ dto.service = chartWidget.getService();
+ dto.refresh = chartWidget.getRefresh();
+ dto.period = chartWidget.getPeriod();
+ dto.legend = chartWidget.getLegend();
+ dto.forceAsItem = chartWidget.getForceAsItem() ? true : null;
+ dto.yAxisDecimalPattern = chartWidget.getYAxisDecimalPattern();
+ dto.interpolation = chartWidget.getInterpolation();
+ break;
+ }
+ case WebviewImpl webviewWidget: {
+ dto.height = webviewWidget.getHeight();
+ dto.url = webviewWidget.getUrl();
+ break;
+ }
+ case SwitchImpl switchWidget: {
+ MappingList mappingList = switchWidget.getMappings();
+ if (mappingList != null && mappingList.getElements() != null && mappingList.getElements().size() > 0) {
+ dto.mappings = mappingList.getElements().stream().map(e -> buildMappingDTO(e))
+ .filter(Objects::nonNull).toList();
+ }
+ break;
+ }
+ case MapviewImpl mapviewWidget: {
+ dto.height = mapviewWidget.getHeight();
+ break;
+ }
+ case SliderImpl sliderWidget: {
+ dto.switchEnabled = sliderWidget.isSwitchEnabled() ? true : null;
+ dto.releaseOnly = sliderWidget.isReleaseOnly() ? true : null;
+ dto.minValue = sliderWidget.getMinValue();
+ dto.maxValue = sliderWidget.getMaxValue();
+ dto.step = sliderWidget.getStep();
+ break;
+ }
+ case SelectionImpl selectionWidget: {
+ MappingList mappingList = selectionWidget.getMappings();
+ if (mappingList != null && mappingList.getElements() != null && mappingList.getElements().size() > 0) {
+ dto.mappings = mappingList.getElements().stream().map(e -> buildMappingDTO(e))
+ .filter(Objects::nonNull).toList();
+ }
+ break;
+ }
+ case SetpointImpl setpointWidget: {
+ dto.minValue = setpointWidget.getMinValue();
+ dto.maxValue = setpointWidget.getMaxValue();
+ dto.step = setpointWidget.getStep();
+ break;
+ }
+ case ColortemperaturepickerImpl colortemperaturepickerWidget: {
+ dto.minValue = colortemperaturepickerWidget.getMinValue();
+ dto.maxValue = colortemperaturepickerWidget.getMaxValue();
+ break;
+ }
+ case InputImpl inputWidget: {
+ dto.inputHint = inputWidget.getInputHint();
+ break;
+ }
+ case ButtongridImpl buttongridWidget: {
+ List> buttons = convertToButtonWidgets(buttongridWidget);
+ if (buttons != null) {
+ dto.widgets = buttons;
+ }
+ break;
+ }
+ case ButtonImpl buttonWidget: {
+ dto.row = buttonWidget.getRow();
+ dto.column = buttonWidget.getColumn();
+ dto.stateless = buttonWidget.isStateless() ? true : null;
+ dto.cmd = buttonWidget.getCmd();
+ dto.releaseCmd = buttonWidget.getReleaseCmd();
+ break;
+ }
+ case DefaultImpl defaultWidget: {
+ dto.height = defaultWidget.getHeight();
+ break;
+ }
+ default:
+ break;
+ }
+
+ ColorArrayList labelColorList = widget.getLabelColor();
+ if (labelColorList != null && labelColorList.getElements() != null && labelColorList.getElements().size() > 0) {
+ dto.labelColor = labelColorList.getElements().stream().map(e -> buildRuleDTO(e)).toList();
+ }
+ ColorArrayList valueColorList = widget.getValueColor();
+ if (valueColorList != null && valueColorList.getElements() != null && valueColorList.getElements().size() > 0) {
+ dto.valueColor = valueColorList.getElements().stream().map(e -> buildRuleDTO(e)).toList();
+ }
+ ColorArrayList iconColorList = widget.getIconColor();
+ if (iconColorList != null && iconColorList.getElements() != null && iconColorList.getElements().size() > 0) {
+ dto.iconColor = iconColorList.getElements().stream().map(e -> buildRuleDTO(e)).toList();
+ }
+ VisibilityRuleList visiblityRuleList = widget.getVisibility();
+ if (visiblityRuleList != null && visiblityRuleList.getElements() != null
+ && visiblityRuleList.getElements().size() > 0) {
+ dto.visibility = visiblityRuleList.getElements().stream().map(e -> buildRuleDTO(e)).toList();
+ }
+
+ if (widget instanceof LinkableWidget linkableWidget) {
+ EList children = linkableWidget.getChildren();
+ if (children != null && !children.isEmpty()) {
+ dto.widgets = children.stream().map(w -> buildWidgetDTO(w)).toList();
+ }
+ }
+
+ String widgetType = widget.getClass().getInterfaces()[0].getSimpleName();
+ return Map.entry(widgetType, dto);
+ }
+
+ private @Nullable YamlRuleDTO buildRuleDTO(T rule) {
+ EList conditions = null;
+ String argument = null;
+ switch (rule) {
+ case IconRule iconRule: {
+ conditions = iconRule.getConditions();
+ argument = iconRule.getArg();
+ break;
+ }
+ case ColorArray colorArray: {
+ conditions = colorArray.getConditions();
+ argument = colorArray.getArg();
+ break;
+ }
+ case VisibilityRule visiblityRule: {
+ conditions = visiblityRule.getConditions();
+ break;
+ }
+ default:
+ break;
+ }
+ if (conditions == null) {
+ return null;
+ }
+ YamlRuleDTO dto = new YamlRuleDTO();
+ dto.conditions = conditions.stream().map(c -> {
+ YamlRuleDTO.Condition condition = dto.new Condition();
+ condition.item = c.getItem();
+ condition.condition = c.getCondition();
+ condition.value = (c.getSign() != null ? c.getSign() : "") + c.getState();
+ return condition;
+ }).toList();
+ dto.argument = argument;
+ return dto;
+ }
+
+ private YamlMappingDTO buildMappingDTO(Mapping mapping) {
+ YamlMappingDTO dto = new YamlMappingDTO();
+ dto.cmd = mapping.getCmd();
+ dto.releaseCmd = mapping.getReleaseCmd();
+ dto.label = mapping.getLabel();
+ dto.icon = mapping.getIcon();
+ return dto;
+ }
+
+ private @Nullable List> convertToButtonWidgets(Buttongrid widget) {
+ String item = widget.getItem();
+ ButtonDefinitionList buttons = widget.getButtons();
+ if (buttons == null || buttons.getElements() == null) {
+ return null;
+ }
+ return buttons.getElements().stream().map(b -> {
+ ButtonImpl buttonWidget = (ButtonImpl) SitemapFactory.eINSTANCE.createButton();
+ buttonWidget.eSet(SitemapPackage.BUTTON__ITEM, item);
+ buttonWidget.eSet(SitemapPackage.BUTTON__ROW, b.getRow());
+ buttonWidget.eSet(SitemapPackage.BUTTON__COLUMN, b.getColumn());
+ buttonWidget.eSet(SitemapPackage.BUTTON__CMD, b.getCmd());
+ buttonWidget.eSet(SitemapPackage.BUTTON__LABEL, b.getLabel());
+ buttonWidget.eSet(SitemapPackage.BUTTON__ICON, b.getIcon());
+ return buildWidgetDTO(buttonWidget);
+ }).toList();
+ }
+}
diff --git a/bundles/org.openhab.core.ui/src/main/java/org/openhab/core/ui/internal/components/UIComponentSitemapProvider.java b/bundles/org.openhab.core.ui/src/main/java/org/openhab/core/ui/internal/components/UIComponentSitemapProvider.java
index 901c4152cb3..9118a35e8b5 100644
--- a/bundles/org.openhab.core.ui/src/main/java/org/openhab/core/ui/internal/components/UIComponentSitemapProvider.java
+++ b/bundles/org.openhab.core.ui/src/main/java/org/openhab/core/ui/internal/components/UIComponentSitemapProvider.java
@@ -24,7 +24,6 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import org.eclipse.emf.common.util.EList;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.common.registry.RegistryChangeListener;
@@ -32,15 +31,15 @@
import org.openhab.core.model.core.EventType;
import org.openhab.core.model.core.ModelRepositoryChangeListener;
import org.openhab.core.model.sitemap.SitemapProvider;
-import org.openhab.core.model.sitemap.sitemap.ButtonDefinition;
-import org.openhab.core.model.sitemap.sitemap.ColorArray;
-import org.openhab.core.model.sitemap.sitemap.IconRule;
+import org.openhab.core.model.sitemap.sitemap.ButtonDefinitionList;
+import org.openhab.core.model.sitemap.sitemap.ColorArrayList;
+import org.openhab.core.model.sitemap.sitemap.IconRuleList;
import org.openhab.core.model.sitemap.sitemap.LinkableWidget;
-import org.openhab.core.model.sitemap.sitemap.Mapping;
+import org.openhab.core.model.sitemap.sitemap.MappingList;
import org.openhab.core.model.sitemap.sitemap.Sitemap;
import org.openhab.core.model.sitemap.sitemap.SitemapFactory;
import org.openhab.core.model.sitemap.sitemap.SitemapPackage;
-import org.openhab.core.model.sitemap.sitemap.VisibilityRule;
+import org.openhab.core.model.sitemap.sitemap.VisibilityRuleList;
import org.openhab.core.model.sitemap.sitemap.Widget;
import org.openhab.core.model.sitemap.sitemap.impl.ButtonDefinitionImpl;
import org.openhab.core.model.sitemap.sitemap.impl.ButtonImpl;
@@ -385,8 +384,8 @@ private void setWidgetIconPropertyFromComponentConfig(Widget widget, @Nullable U
}
}
- private void addWidgetMappings(EList mappings, UIComponent component) {
- if (component.getConfig() != null && component.getConfig().containsKey("mappings")) {
+ private void addWidgetMappings(@Nullable MappingList mappings, UIComponent component) {
+ if (mappings != null && component.getConfig() != null && component.getConfig().containsKey("mappings")) {
Object sourceMappings = component.getConfig().get("mappings");
if (sourceMappings instanceof Collection> sourceMappingsCollection) {
for (Object sourceMapping : sourceMappingsCollection) {
@@ -408,15 +407,15 @@ private void addWidgetMappings(EList mappings, UIComponent component) {
mapping.setReleaseCmd(releaseCmd);
mapping.setLabel(label);
mapping.setIcon(icon);
- mappings.add(mapping);
+ mappings.getElements().add(mapping);
}
}
}
}
}
- private void addWidgetButtons(EList buttons, UIComponent component) {
- if (component.getConfig() != null && component.getConfig().containsKey("buttons")) {
+ private void addWidgetButtons(@Nullable ButtonDefinitionList buttons, UIComponent component) {
+ if (buttons != null && component.getConfig() != null && component.getConfig().containsKey("buttons")) {
Object sourceButtons = component.getConfig().get("buttons");
if (sourceButtons instanceof Collection> sourceButtonsCollection) {
for (Object sourceButton : sourceButtonsCollection) {
@@ -435,15 +434,15 @@ private void addWidgetButtons(EList buttons, UIComponent compo
button.setCmd(cmd);
button.setLabel(label);
button.setIcon(icon);
- buttons.add(button);
+ buttons.getElements().add(button);
}
}
}
}
}
- private void addWidgetVisibility(EList visibility, UIComponent component) {
- if (component.getConfig() != null && component.getConfig().containsKey("visibility")) {
+ private void addWidgetVisibility(@Nullable VisibilityRuleList visibility, UIComponent component) {
+ if (visibility != null && component.getConfig() != null && component.getConfig().containsKey("visibility")) {
Object sourceVisibilities = component.getConfig().get("visibility");
if (sourceVisibilities instanceof Collection> sourceVisibilitiesCollection) {
for (Object sourceVisibility : sourceVisibilitiesCollection) {
@@ -453,27 +452,27 @@ private void addWidgetVisibility(EList visibility, UIComponent c
.createVisibilityRule();
List conditions = getConditions(conditionsString, component, "visibility");
visibilityRule.eSet(SitemapPackage.VISIBILITY_RULE__CONDITIONS, conditions);
- visibility.add(visibilityRule);
+ visibility.getElements().add(visibilityRule);
}
}
}
}
}
- private void addLabelColor(EList labelColor, UIComponent component) {
+ private void addLabelColor(@Nullable ColorArrayList labelColor, UIComponent component) {
addColor(labelColor, component, "labelcolor");
}
- private void addValueColor(EList valueColor, UIComponent component) {
+ private void addValueColor(@Nullable ColorArrayList valueColor, UIComponent component) {
addColor(valueColor, component, "valuecolor");
}
- private void addIconColor(EList iconColor, UIComponent component) {
+ private void addIconColor(@Nullable ColorArrayList iconColor, UIComponent component) {
addColor(iconColor, component, "iconcolor");
}
- private void addColor(EList color, UIComponent component, String key) {
- if (component.getConfig() != null && component.getConfig().containsKey(key)) {
+ private void addColor(@Nullable ColorArrayList color, UIComponent component, String key) {
+ if (color != null && component.getConfig() != null && component.getConfig().containsKey(key)) {
Object sourceColors = component.getConfig().get(key);
if (sourceColors instanceof Collection> sourceColorsCollection) {
for (Object sourceColor : sourceColorsCollection) {
@@ -484,15 +483,15 @@ private void addColor(EList color, UIComponent component, String key
colorArray.setArg(argument);
List conditions = getConditions(conditionsString, component, key);
colorArray.eSet(SitemapPackage.COLOR_ARRAY__CONDITIONS, conditions);
- color.add(colorArray);
+ color.getElements().add(colorArray);
}
}
}
}
}
- private void addIconRules(EList icon, UIComponent component) {
- if (component.getConfig() != null && component.getConfig().containsKey("iconrules")) {
+ private void addIconRules(@Nullable IconRuleList icon, UIComponent component) {
+ if (icon != null && component.getConfig() != null && component.getConfig().containsKey("iconrules")) {
Object sourceIcons = component.getConfig().get("iconrules");
if (sourceIcons instanceof Collection> sourceIconsCollection) {
for (Object sourceIcon : sourceIconsCollection) {
@@ -503,7 +502,7 @@ private void addIconRules(EList icon, UIComponent component) {
iconRule.setArg(argument);
List conditions = getConditions(conditionsString, component, "iconrules");
iconRule.eSet(SitemapPackage.ICON_RULE__CONDITIONS, conditions);
- icon.add(iconRule);
+ icon.getElements().add(iconRule);
}
}
}
diff --git a/bundles/org.openhab.core.ui/src/main/java/org/openhab/core/ui/internal/items/ItemUIRegistryImpl.java b/bundles/org.openhab.core.ui/src/main/java/org/openhab/core/ui/internal/items/ItemUIRegistryImpl.java
index f78dc067be8..b06ee6adc8a 100644
--- a/bundles/org.openhab.core.ui/src/main/java/org/openhab/core/ui/internal/items/ItemUIRegistryImpl.java
+++ b/bundles/org.openhab.core.ui/src/main/java/org/openhab/core/ui/internal/items/ItemUIRegistryImpl.java
@@ -65,16 +65,20 @@
import org.openhab.core.library.types.PlayPauseType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.model.sitemap.sitemap.ColorArray;
+import org.openhab.core.model.sitemap.sitemap.ColorArrayList;
import org.openhab.core.model.sitemap.sitemap.Default;
import org.openhab.core.model.sitemap.sitemap.Group;
import org.openhab.core.model.sitemap.sitemap.IconRule;
+import org.openhab.core.model.sitemap.sitemap.IconRuleList;
import org.openhab.core.model.sitemap.sitemap.LinkableWidget;
import org.openhab.core.model.sitemap.sitemap.Mapping;
+import org.openhab.core.model.sitemap.sitemap.MappingList;
import org.openhab.core.model.sitemap.sitemap.Sitemap;
import org.openhab.core.model.sitemap.sitemap.SitemapFactory;
import org.openhab.core.model.sitemap.sitemap.Slider;
import org.openhab.core.model.sitemap.sitemap.Switch;
import org.openhab.core.model.sitemap.sitemap.VisibilityRule;
+import org.openhab.core.model.sitemap.sitemap.VisibilityRuleList;
import org.openhab.core.model.sitemap.sitemap.Widget;
import org.openhab.core.transform.TransformationException;
import org.openhab.core.transform.TransformationHelper;
@@ -323,7 +327,8 @@ && hasItemTag(itemName, "ColorTemperature")) {
private Switch createPlayerButtons() {
final Switch playerItemSwitch = SitemapFactory.eINSTANCE.createSwitch();
- final List mappings = playerItemSwitch.getMappings();
+ final MappingList mappingList = SitemapFactory.eINSTANCE.createMappingList();
+ final List mappings = mappingList.getElements();
Mapping commandMapping;
mappings.add(commandMapping = SitemapFactory.eINSTANCE.createMapping());
commandMapping.setCmd(NextPreviousType.PREVIOUS.name());
@@ -337,6 +342,7 @@ private Switch createPlayerButtons() {
mappings.add(commandMapping = SitemapFactory.eINSTANCE.createMapping());
commandMapping.setCmd(NextPreviousType.NEXT.name());
commandMapping.setLabel(">>");
+ playerItemSwitch.setMappings(mappingList);
return playerItemSwitch;
}
@@ -754,7 +760,8 @@ private String transform(String label, boolean matchTransform, @Nullable String
}
} else if (w instanceof Switch sw) {
StateDescription stateDescr = i.getStateDescription();
- if (sw.getMappings().isEmpty() && (stateDescr == null || stateDescr.getOptions().isEmpty())) {
+ if ((sw.getMappings() == null || sw.getMappings().getElements().isEmpty())
+ && (stateDescr == null || stateDescr.getOptions().isEmpty())) {
returnState = itemState.as(OnOffType.class);
}
}
@@ -853,11 +860,11 @@ private void copyProperties(Widget source, Widget target) {
target.setIcon(source.getIcon());
target.setStaticIcon(source.getStaticIcon());
target.setLabel(source.getLabel());
- target.getVisibility().addAll(EcoreUtil.copyAll(source.getVisibility()));
- target.getLabelColor().addAll(EcoreUtil.copyAll(source.getLabelColor()));
- target.getValueColor().addAll(EcoreUtil.copyAll(source.getValueColor()));
- target.getIconColor().addAll(EcoreUtil.copyAll(source.getIconColor()));
- target.getIconRules().addAll(EcoreUtil.copyAll(source.getIconRules()));
+ target.setVisibility(EcoreUtil.copy(source.getVisibility()));
+ target.setLabelColor(EcoreUtil.copy(source.getLabelColor()));
+ target.setValueColor(EcoreUtil.copy(source.getValueColor()));
+ target.setIconColor(EcoreUtil.copy(source.getIconColor()));
+ target.setIconRules(EcoreUtil.copy(source.getIconRules()));
}
/**
@@ -1207,9 +1214,14 @@ private boolean matchStateToValue(State state, String value, @Nullable String ma
return matched;
}
- private @Nullable String processColorDefinition(Widget w, @Nullable List colorList, String colorType) {
+ private @Nullable String processColorDefinition(Widget w, @Nullable ColorArrayList colorArrayList,
+ String colorType) {
+ if (colorArrayList == null) {
+ return null;
+ }
+ List colorList = colorArrayList.getElements();
// Sanity check
- if (colorList == null || colorList.isEmpty()) {
+ if (colorList.isEmpty()) {
return null;
}
@@ -1258,9 +1270,13 @@ private boolean matchStateToValue(State state, String value, @Nullable String ma
@Override
public boolean getVisiblity(Widget w) {
+ VisibilityRuleList visibilityRuleList = w.getVisibility();
// Default to visible if parameters not set
- List ruleList = w.getVisibility();
- if (ruleList == null || ruleList.isEmpty()) {
+ if (visibilityRuleList == null) {
+ return true;
+ }
+ List ruleList = visibilityRuleList.getElements();
+ if (ruleList.isEmpty()) {
return true;
}
@@ -1279,9 +1295,13 @@ public boolean getVisiblity(Widget w) {
@Override
public @Nullable String getConditionalIcon(Widget w) {
- List ruleList = w.getIconRules();
+ IconRuleList iconRuleList = w.getIconRules();
+ if (iconRuleList == null) {
+ return null;
+ }
+ List ruleList = iconRuleList.getElements();
// Sanity check
- if (ruleList == null || ruleList.isEmpty()) {
+ if (ruleList.isEmpty()) {
return null;
}
diff --git a/bundles/org.openhab.core.ui/src/test/java/org/openhab/core/ui/internal/items/ItemUIRegistryImplTest.java b/bundles/org.openhab.core.ui/src/test/java/org/openhab/core/ui/internal/items/ItemUIRegistryImplTest.java
index baef93ca74b..88df324c7f9 100644
--- a/bundles/org.openhab.core.ui/src/test/java/org/openhab/core/ui/internal/items/ItemUIRegistryImplTest.java
+++ b/bundles/org.openhab.core.ui/src/test/java/org/openhab/core/ui/internal/items/ItemUIRegistryImplTest.java
@@ -62,12 +62,15 @@
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.model.sitemap.sitemap.ColorArray;
+import org.openhab.core.model.sitemap.sitemap.ColorArrayList;
import org.openhab.core.model.sitemap.sitemap.Colorpicker;
import org.openhab.core.model.sitemap.sitemap.Condition;
import org.openhab.core.model.sitemap.sitemap.Group;
import org.openhab.core.model.sitemap.sitemap.IconRule;
+import org.openhab.core.model.sitemap.sitemap.IconRuleList;
import org.openhab.core.model.sitemap.sitemap.Image;
import org.openhab.core.model.sitemap.sitemap.Mapping;
+import org.openhab.core.model.sitemap.sitemap.MappingList;
import org.openhab.core.model.sitemap.sitemap.Mapview;
import org.openhab.core.model.sitemap.sitemap.Selection;
import org.openhab.core.model.sitemap.sitemap.Sitemap;
@@ -76,6 +79,7 @@
import org.openhab.core.model.sitemap.sitemap.Switch;
import org.openhab.core.model.sitemap.sitemap.Text;
import org.openhab.core.model.sitemap.sitemap.VisibilityRule;
+import org.openhab.core.model.sitemap.sitemap.VisibilityRuleList;
import org.openhab.core.model.sitemap.sitemap.Widget;
import org.openhab.core.types.CommandDescriptionBuilder;
import org.openhab.core.types.CommandOption;
@@ -591,7 +595,9 @@ public void testStateConversionForSwitchWidgetThroughGetState() throws ItemNotFo
Switch switchWidget = mock(Switch.class);
when(switchWidget.getItem()).thenReturn("myItem");
- when(switchWidget.getMappings()).thenReturn(new BasicEList<>());
+ MappingList mappingList = mock(MappingList.class);
+ when(switchWidget.getMappings()).thenReturn(mappingList);
+ when(switchWidget.getMappings().getElements()).thenReturn(new BasicEList<>());
State stateForSwitch = uiRegistry.getState(switchWidget);
@@ -614,7 +620,9 @@ public void testStateConversionForSwitchWidgetWithMappingThroughGetState() throw
Mapping mapping = mock(Mapping.class);
BasicEList mappings = new BasicEList<>();
mappings.add(mapping);
- when(switchWidget.getMappings()).thenReturn(mappings);
+ MappingList mappingList = mock(MappingList.class);
+ when(switchWidget.getMappings()).thenReturn(mappingList);
+ when(switchWidget.getMappings().getElements()).thenReturn(mappings);
State stateForSwitch = uiRegistry.getState(switchWidget);
@@ -868,7 +876,9 @@ public void getLabelColorLabelWithDecimalValue() {
when(rule.getArg()).thenReturn("yellow");
BasicEList rules = new BasicEList<>();
rules.add(rule);
- when(widgetMock.getLabelColor()).thenReturn(rules);
+ ColorArrayList colorArrayList = mock(ColorArrayList.class);
+ when(widgetMock.getLabelColor()).thenReturn(colorArrayList);
+ when(widgetMock.getLabelColor().getElements()).thenReturn(rules);
when(itemMock.getState()).thenReturn(new DecimalType(10f / 3f));
@@ -897,7 +907,9 @@ public void getLabelColorLabelWithUnitValue() {
when(rule.getArg()).thenReturn("yellow");
BasicEList rules = new BasicEList<>();
rules.add(rule);
- when(widgetMock.getLabelColor()).thenReturn(rules);
+ ColorArrayList colorArrayList = mock(ColorArrayList.class);
+ when(widgetMock.getLabelColor()).thenReturn(colorArrayList);
+ when(widgetMock.getLabelColor().getElements()).thenReturn(rules);
when(itemMock.getState()).thenReturn(new QuantityType<>("20 °C"));
@@ -940,7 +952,7 @@ public void getDefaultWidgets() {
defaultWidget = uiRegistry.getDefaultWidget(PlayerItem.class, ITEM_NAME);
assertThat(defaultWidget, is(instanceOf(Switch.class)));
- assertThat(((Switch) defaultWidget).getMappings(), hasSize(4));
+ assertThat(((Switch) defaultWidget).getMappings().getElements(), hasSize(4));
defaultWidget = uiRegistry.getDefaultWidget(RollershutterItem.class, ITEM_NAME);
assertThat(defaultWidget, is(instanceOf(Switch.class)));
@@ -1098,7 +1110,9 @@ public void getLabelColorDefaultColor() {
when(rule3.getConditions()).thenReturn(conditions5);
when(rule3.getArg()).thenReturn("blue");
rules.add(rule3);
- when(widgetMock.getLabelColor()).thenReturn(rules);
+ ColorArrayList colorArrayList = mock(ColorArrayList.class);
+ when(widgetMock.getLabelColor()).thenReturn(colorArrayList);
+ when(widgetMock.getLabelColor().getElements()).thenReturn(rules);
when(itemMock.getState()).thenReturn(new DecimalType(20.9));
@@ -1165,7 +1179,9 @@ public void getValueColor() {
when(rule3.getConditions()).thenReturn(conditions5);
when(rule3.getArg()).thenReturn("blue");
rules.add(rule3);
- when(widgetMock.getValueColor()).thenReturn(rules);
+ ColorArrayList colorArrayList = mock(ColorArrayList.class);
+ when(widgetMock.getValueColor()).thenReturn(colorArrayList);
+ when(widgetMock.getValueColor().getElements()).thenReturn(rules);
when(itemMock.getState()).thenReturn(new DecimalType(20.9));
@@ -1232,7 +1248,9 @@ public void getIconColor() {
when(rule3.getConditions()).thenReturn(conditions5);
when(rule3.getArg()).thenReturn("blue");
rules.add(rule3);
- when(widgetMock.getIconColor()).thenReturn(rules);
+ ColorArrayList colorArrayList = mock(ColorArrayList.class);
+ when(widgetMock.getIconColor()).thenReturn(colorArrayList);
+ when(widgetMock.getIconColor().getElements()).thenReturn(rules);
when(itemMock.getState()).thenReturn(new DecimalType(20.9));
@@ -1280,7 +1298,9 @@ public void getVisibility() {
when(rule.getConditions()).thenReturn(conditions);
BasicEList rules = new BasicEList<>();
rules.add(rule);
- when(widgetMock.getVisibility()).thenReturn(rules);
+ VisibilityRuleList visibilityRuleList = mock(VisibilityRuleList.class);
+ when(widgetMock.getVisibility()).thenReturn(visibilityRuleList);
+ when(widgetMock.getVisibility().getElements()).thenReturn(rules);
when(itemMock.getState()).thenReturn(new DecimalType(20.9));
@@ -1340,7 +1360,9 @@ public void getCategoryWhenIconSetWithRules() {
when(rule2.getConditions()).thenReturn(conditions2);
when(rule2.getArg()).thenReturn("humidity");
rules.add(rule2);
- when(widgetMock.getIconRules()).thenReturn(rules);
+ IconRuleList iconRuleList = mock(IconRuleList.class);
+ when(widgetMock.getIconRules()).thenReturn(iconRuleList);
+ when(widgetMock.getIconRules().getElements()).thenReturn(rules);
when(itemMock.getState()).thenReturn(new DecimalType(20.9));