mirror of
https://github.com/danieldemus/openhab-core.git
synced 2026-07-29 12:34:22 +02:00
Add support for short-form expire property in Item Yaml config (#5252)
* Add support for short-form expire property in Item Yaml config Signed-off-by: Jimmy Tanagra <jcode@tanagra.id.au>
This commit is contained in:
+13
-6
@@ -49,6 +49,7 @@ public class YamlItemDTO implements YamlElement, Cloneable {
|
||||
public String icon;
|
||||
public String format;
|
||||
public String unit;
|
||||
public String expire;
|
||||
public Boolean autoupdate;
|
||||
public List<@NonNull String> groups;
|
||||
public Set<@NonNull String> tags;
|
||||
@@ -195,6 +196,12 @@ public class YamlItemDTO implements YamlElement, Cloneable {
|
||||
"item \"%s\": \"format\" field is redundant with pattern in \"stateDescription\" metadata; \"%s\" will be considered"
|
||||
.formatted(name, pattern));
|
||||
}
|
||||
md = metadata.get("expire");
|
||||
if (md != null && expire != null) {
|
||||
addToList(warnings,
|
||||
"item \"%s\": \"expire\" field is redundant with \"expire\" metadata; value \"%s\" will be considered"
|
||||
.formatted(name, md.getValue()));
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
@@ -266,8 +273,8 @@ public class YamlItemDTO implements YamlElement, Cloneable {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(name, getType(), group, label, icon, format, unit, autoupdate, getGroups(), getTags(),
|
||||
channel);
|
||||
return Objects.hash(name, getType(), group, label, icon, format, unit, expire, autoupdate, getGroups(),
|
||||
getTags(), channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -281,10 +288,10 @@ public class YamlItemDTO implements YamlElement, Cloneable {
|
||||
return Objects.equals(name, other.name) && Objects.equals(getType(), other.getType())
|
||||
&& Objects.equals(group, other.group) && Objects.equals(label, other.label)
|
||||
&& Objects.equals(icon, other.icon) && Objects.equals(format, other.format)
|
||||
&& Objects.equals(unit, other.unit) && Objects.equals(autoupdate, other.autoupdate)
|
||||
&& Objects.equals(getGroups(), other.getGroups()) && Objects.equals(getTags(), other.getTags())
|
||||
&& Objects.equals(channel, other.channel) && equalsChannels(channels, other.channels)
|
||||
&& equalsMetadata(metadata, other.metadata);
|
||||
&& Objects.equals(unit, other.unit) && Objects.equals(expire, other.expire)
|
||||
&& Objects.equals(autoupdate, other.autoupdate) && Objects.equals(getGroups(), other.getGroups())
|
||||
&& Objects.equals(getTags(), other.getTags()) && Objects.equals(channel, other.channel)
|
||||
&& equalsChannels(channels, other.channels) && equalsMetadata(metadata, other.metadata);
|
||||
}
|
||||
|
||||
private boolean equalsChannels(@Nullable Map<@NonNull String, @NonNull Map<@NonNull String, @NonNull Object>> first,
|
||||
|
||||
+8
@@ -264,6 +264,7 @@ public class YamlItemProvider extends AbstractProvider<Item> implements ItemProv
|
||||
boolean hasAutoUpdateMetadata = false;
|
||||
boolean hasUnitMetadata = false;
|
||||
boolean hasStateDescriptionMetadata = false;
|
||||
boolean hasExpireMetadata = false;
|
||||
if (itemDTO.metadata != null) {
|
||||
for (Map.Entry<String, YamlMetadataDTO> entry : itemDTO.metadata.entrySet()) {
|
||||
if ("autoupdate".equals(entry.getKey())) {
|
||||
@@ -272,6 +273,8 @@ public class YamlItemProvider extends AbstractProvider<Item> implements ItemProv
|
||||
hasUnitMetadata = true;
|
||||
} else if ("stateDescription".equals(entry.getKey())) {
|
||||
hasStateDescriptionMetadata = true;
|
||||
} else if ("expire".equals(entry.getKey())) {
|
||||
hasExpireMetadata = true;
|
||||
}
|
||||
Map<String, Object> config = entry.getValue().config;
|
||||
if (itemDTO.format != null && "stateDescription".equals(entry.getKey())
|
||||
@@ -305,6 +308,11 @@ public class YamlItemProvider extends AbstractProvider<Item> implements ItemProv
|
||||
mdDTO.config = Map.of("pattern", itemDTO.format);
|
||||
metadata.put("stateDescription", mdDTO);
|
||||
}
|
||||
if (!hasExpireMetadata && itemDTO.expire != null) {
|
||||
YamlMetadataDTO mdDTO = new YamlMetadataDTO();
|
||||
mdDTO.value = itemDTO.expire;
|
||||
metadata.put("expire", mdDTO);
|
||||
}
|
||||
}
|
||||
metaDataProvider.updateMetadata(modelName, itemName, metadata);
|
||||
}
|
||||
|
||||
+18
-3
@@ -184,13 +184,28 @@ public class YamlItemFileConverter extends AbstractItemFileGenerator implements
|
||||
Map<String, YamlMetadataDTO> metadataDto = new LinkedHashMap<>();
|
||||
for (Metadata md : metadata) {
|
||||
String namespace = md.getUID().getNamespace();
|
||||
String value = md.getValue();
|
||||
if ("autoupdate".equals(namespace)) {
|
||||
dto.autoupdate = Boolean.valueOf(md.getValue());
|
||||
// When autoupdate value is an empty string, treat it as not set since dto.autoupdate only accepts
|
||||
// true/false
|
||||
if (value != null && !value.isEmpty()) {
|
||||
dto.autoupdate = Boolean.valueOf(value);
|
||||
}
|
||||
} else if ("unit".equals(namespace)) {
|
||||
dto.unit = md.getValue();
|
||||
dto.unit = value; // When unit value is empty string, keep it as empty string
|
||||
} else if ("expire".equals(namespace)) {
|
||||
Map<String, Object> configuration = md.getConfiguration();
|
||||
if (configuration.isEmpty()) {
|
||||
dto.expire = value; // When expire value is empty string, keep it as empty string
|
||||
} else {
|
||||
YamlMetadataDTO mdDto = new YamlMetadataDTO();
|
||||
mdDto.value = value.isEmpty() ? null : value;
|
||||
mdDto.config = configuration;
|
||||
metadataDto.put(namespace, mdDto);
|
||||
}
|
||||
} else {
|
||||
YamlMetadataDTO mdDto = new YamlMetadataDTO();
|
||||
mdDto.value = md.getValue().isEmpty() ? null : md.getValue();
|
||||
mdDto.value = value.isEmpty() ? null : value;
|
||||
Map<String, Object> configuration = new LinkedHashMap<>();
|
||||
String statePattern = null;
|
||||
for (ConfigParameter param : getConfigurationParameters(md)) {
|
||||
|
||||
+51
@@ -15,6 +15,7 @@ package org.openhab.core.model.yaml.internal.items;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -376,6 +377,34 @@ public class YamlItemDTOTest {
|
||||
assertEquals(item1.hashCode(), item2.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEqualsWithExpire() throws IOException {
|
||||
YamlItemDTO item1 = new YamlItemDTO();
|
||||
YamlItemDTO item2 = new YamlItemDTO();
|
||||
|
||||
item1.name = "item_name";
|
||||
item2.name = "item_name";
|
||||
item1.type = "Switch";
|
||||
item2.type = "Switch";
|
||||
|
||||
item1.expire = null;
|
||||
item2.expire = null;
|
||||
assertTrue(item1.equals(item2));
|
||||
assertEquals(item1.hashCode(), item2.hashCode());
|
||||
item1.expire = "5m";
|
||||
item2.expire = null;
|
||||
assertFalse(item1.equals(item2));
|
||||
item1.expire = null;
|
||||
item2.expire = "5m";
|
||||
assertFalse(item1.equals(item2));
|
||||
item1.expire = "5m";
|
||||
item2.expire = "1h";
|
||||
assertFalse(item1.equals(item2));
|
||||
item2.expire = "5m";
|
||||
assertTrue(item1.equals(item2));
|
||||
assertEquals(item1.hashCode(), item2.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEqualsWithGroups() throws IOException {
|
||||
YamlItemDTO item1 = new YamlItemDTO();
|
||||
@@ -551,4 +580,26 @@ public class YamlItemDTOTest {
|
||||
item2.metadata = Map.of("namespace", md3, "namespace2", md2);
|
||||
assertFalse(item1.equals(item2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExpireMetadataWarning() throws IOException {
|
||||
YamlItemDTO item = new YamlItemDTO();
|
||||
item.name = "item_name";
|
||||
item.type = "String";
|
||||
item.expire = "10m";
|
||||
|
||||
YamlMetadataDTO md = new YamlMetadataDTO();
|
||||
md.value = "5m";
|
||||
item.metadata = Map.of("expire", md);
|
||||
|
||||
List<String> errors = new ArrayList<>();
|
||||
List<String> warnings = new ArrayList<>();
|
||||
|
||||
assertTrue(item.isValid(errors, warnings));
|
||||
assertTrue(errors.isEmpty());
|
||||
assertEquals(1, warnings.size());
|
||||
assertEquals(
|
||||
"item \"item_name\": \"expire\" field is redundant with \"expire\" metadata; value \"5m\" will be considered",
|
||||
warnings.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
+167
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2026 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.items.fileconverter;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.core.config.core.ConfigDescriptionRegistry;
|
||||
import org.openhab.core.items.Item;
|
||||
import org.openhab.core.items.Metadata;
|
||||
import org.openhab.core.items.MetadataKey;
|
||||
import org.openhab.core.model.yaml.YamlElement;
|
||||
import org.openhab.core.model.yaml.YamlModelRepository;
|
||||
import org.openhab.core.model.yaml.internal.items.YamlChannelLinkProvider;
|
||||
import org.openhab.core.model.yaml.internal.items.YamlItemDTO;
|
||||
import org.openhab.core.model.yaml.internal.items.YamlItemProvider;
|
||||
import org.openhab.core.model.yaml.internal.items.YamlMetadataProvider;
|
||||
|
||||
@NonNullByDefault
|
||||
public class YamlItemFileConverterTest {
|
||||
|
||||
@Test
|
||||
public void testExpireMetadataConvertedToShortForm() {
|
||||
Metadata expireMetadata = new Metadata(new MetadataKey("expire", "item_name"), "10m", Map.of());
|
||||
YamlItemDTO dto = convertWithMetadata(expireMetadata, "String");
|
||||
assertEquals("10m", dto.expire);
|
||||
assertNull(dto.metadata);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExpireMetadataEmptyStringStaysInShortForm() {
|
||||
Metadata expireMetadata = new Metadata(new MetadataKey("expire", "item_name"), "", Map.of());
|
||||
YamlItemDTO dto = convertWithMetadata(expireMetadata, "String");
|
||||
assertEquals("", dto.expire);
|
||||
assertNull(dto.metadata);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExpireMetadataWithConfigStaysMetadata() {
|
||||
Metadata expireMetadata = new Metadata(new MetadataKey("expire", "item_name"), "10m", Map.of("command", "OFF"));
|
||||
YamlItemDTO dto = convertWithMetadata(expireMetadata, "String");
|
||||
assertNull(dto.expire);
|
||||
assertNotNull(dto.metadata);
|
||||
assertTrue(dto.metadata.containsKey("expire"));
|
||||
assertEquals("10m", dto.metadata.get("expire").getValue());
|
||||
assertEquals("OFF", dto.metadata.get("expire").config.get("command"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAutoupdateMetadataSetsField() {
|
||||
Metadata autoupdate = new Metadata(new MetadataKey("autoupdate", "item_name"), "true", Map.of());
|
||||
YamlItemDTO dto = convertWithMetadata(autoupdate, "String");
|
||||
assertEquals(Boolean.TRUE, dto.autoupdate);
|
||||
assertNull(dto.metadata);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAutoupdateMetadataEmptyStringIsTreatedAsNotSet() {
|
||||
Metadata autoupdate = new Metadata(new MetadataKey("autoupdate", "item_name"), "", Map.of());
|
||||
YamlItemDTO dto = convertWithMetadata(autoupdate, "String");
|
||||
assertNull(dto.autoupdate);
|
||||
assertNull(dto.metadata);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnitMetadataSetsField() {
|
||||
Metadata unit = new Metadata(new MetadataKey("unit", "item_name"), "kWh", Map.of());
|
||||
YamlItemDTO dto = convertWithMetadata(unit, "Number");
|
||||
assertEquals("kWh", dto.unit);
|
||||
assertNull(dto.metadata);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnitMetadataEmptyStringStaysInShortForm() {
|
||||
Metadata unit = new Metadata(new MetadataKey("unit", "item_name"), "", Map.of());
|
||||
YamlItemDTO dto = convertWithMetadata(unit, "Number");
|
||||
assertEquals("", dto.unit);
|
||||
assertNull(dto.metadata);
|
||||
}
|
||||
|
||||
private YamlItemDTO convertWithMetadata(Metadata metadata, String itemType) {
|
||||
CapturingYamlModelRepository repository = new CapturingYamlModelRepository();
|
||||
YamlItemFileConverter converter = new YamlItemFileConverter(repository, mock(YamlItemProvider.class),
|
||||
mock(YamlMetadataProvider.class), mock(YamlChannelLinkProvider.class),
|
||||
mock(ConfigDescriptionRegistry.class));
|
||||
|
||||
Item item = mock(Item.class);
|
||||
when(item.getName()).thenReturn(metadata.getUID().getItemName());
|
||||
when(item.getLabel()).thenReturn(null);
|
||||
when(item.getType()).thenReturn(itemType);
|
||||
when(item.getCategory()).thenReturn(null);
|
||||
when(item.getGroupNames()).thenReturn(List.of());
|
||||
when(item.getTags()).thenReturn(Set.of());
|
||||
|
||||
converter.setItemsToBeGenerated("id", List.of(item), List.of(metadata), Map.of(), false);
|
||||
|
||||
List<YamlElement> elements = repository.getElements();
|
||||
assertEquals(1, elements.size());
|
||||
assertInstanceOf(YamlItemDTO.class, elements.get(0));
|
||||
return (YamlItemDTO) elements.get(0);
|
||||
}
|
||||
|
||||
private static class CapturingYamlModelRepository implements YamlModelRepository {
|
||||
|
||||
private List<YamlElement> elements = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void addElementToModel(String modelName, YamlElement element) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeElementFromModel(String modelName, YamlElement element) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateElementInModel(String modelName, YamlElement element) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addElementsToBeGenerated(String id, List<YamlElement> elements) {
|
||||
this.elements = elements;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateFileFormat(String id, OutputStream out) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String createIsolatedModel(InputStream inputStream, List<String> errors,
|
||||
List<String> warnings) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeIsolatedModel(String modelName) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
public List<YamlElement> getElements() {
|
||||
return elements;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user