diff --git a/bundles/org.openhab.core.io.rest.core/src/main/java/org/openhab/core/io/rest/core/internal/persistence/PersistenceResource.java b/bundles/org.openhab.core.io.rest.core/src/main/java/org/openhab/core/io/rest/core/internal/persistence/PersistenceResource.java index 7cb4f68e4a3..de683d44403 100644 --- a/bundles/org.openhab.core.io.rest.core/src/main/java/org/openhab/core/io/rest/core/internal/persistence/PersistenceResource.java +++ b/bundles/org.openhab.core.io.rest.core/src/main/java/org/openhab/core/io/rest/core/internal/persistence/PersistenceResource.java @@ -16,9 +16,13 @@ import java.time.LocalDateTime; import java.time.ZonedDateTime; import java.util.ArrayList; +import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; import javax.annotation.security.RolesAllowed; import javax.ws.rs.Consumes; @@ -104,6 +108,7 @@ * @author Lyubomir Papazov - Change java.util.Date references to be of type java.time.ZonedDateTime * @author Markus Rathgeb - Migrated to JAX-RS Whiteboard Specification * @author Wouter Born - Migrated to OpenAPI annotations + * @author Mark Herwege - Implement aliases */ @Component @JaxrsResource @@ -181,9 +186,10 @@ public Response httpGetPersistenceServiceConfiguration(@Context HttpHeaders head PersistenceService service = persistenceServiceRegistry.get(serviceId); if (service != null) { List strategies = service.getDefaultStrategies(); - List configs = List.of( - new PersistenceItemConfiguration(List.of(new PersistenceAllConfig()), null, strategies, null)); - configuration = new PersistenceServiceConfiguration(serviceId, configs, strategies, strategies, + List configs = List + .of(new PersistenceItemConfiguration(List.of(new PersistenceAllConfig()), strategies, null)); + Map aliases = Map.of(); + configuration = new PersistenceServiceConfiguration(serviceId, configs, aliases, strategies, strategies, List.of()); editable = true; } @@ -368,6 +374,9 @@ private Response getItemHistoryDTO(@Nullable String serviceId, String itemName, // If serviceId is null, then use the default service PersistenceService service; String effectiveServiceId = serviceId != null ? serviceId : persistenceServiceRegistry.getDefaultId(); + if (effectiveServiceId == null) { + return null; + } service = persistenceServiceRegistry.get(effectiveServiceId); if (service == null) { @@ -416,6 +425,8 @@ private Response getItemHistoryDTO(@Nullable String serviceId, String itemName, ItemHistoryDTO dto = new ItemHistoryDTO(); dto.name = itemName; + PersistenceServiceConfiguration config = persistenceServiceConfigurationRegistry.get(effectiveServiceId); + String alias = config != null ? config.getAliases().get(itemName) : null; // If "boundary" is true then we want to get one value before and after the requested period // This is necessary for values that don't change often otherwise data will start after the start of the graph @@ -427,7 +438,7 @@ private Response getItemHistoryDTO(@Nullable String serviceId, String itemName, filterBeforeStart.setEndDate(dateTimeBegin); filterBeforeStart.setPageSize(1); filterBeforeStart.setOrdering(Ordering.DESCENDING); - result = qService.query(filterBeforeStart); + result = qService.query(filterBeforeStart, alias); if (result.iterator().hasNext()) { dto.addData(dateTimeBegin.toInstant().toEpochMilli(), result.iterator().next().getState()); quantity++; @@ -446,7 +457,7 @@ private Response getItemHistoryDTO(@Nullable String serviceId, String itemName, filter.setBeginDate(dateTimeBegin); filter.setEndDate(dateTimeEnd); filter.setOrdering(Ordering.ASCENDING); - result = qService.query(filter); + result = qService.query(filter, alias); Iterator it = result.iterator(); // Iterate through the data @@ -478,7 +489,7 @@ private Response getItemHistoryDTO(@Nullable String serviceId, String itemName, filterAfterEnd.setBeginDate(dateTimeEnd); filterAfterEnd.setPageSize(1); filterAfterEnd.setOrdering(Ordering.ASCENDING); - result = qService.query(filterAfterEnd); + result = qService.query(filterAfterEnd, alias); if (result.iterator().hasNext()) { dto.addData(dateTimeEnd.toInstant().toEpochMilli(), result.iterator().next().getState()); quantity++; @@ -544,26 +555,60 @@ private List getPersistenceServiceList(Locale locale) { private Response getServiceItemList(@Nullable String serviceId) { // If serviceId is null, then use the default service PersistenceService service; - if (serviceId == null) { - service = persistenceServiceRegistry.getDefault(); - } else { - service = persistenceServiceRegistry.get(serviceId); + String effectiveServiceId = serviceId != null ? serviceId : persistenceServiceRegistry.getDefaultId(); + if (effectiveServiceId == null) { + logger.debug("Persistence service not found '{}'.", effectiveServiceId); + return JSONResponse.createErrorResponse(Status.BAD_REQUEST, + "Persistence service not found: " + effectiveServiceId); } + service = persistenceServiceRegistry.get(effectiveServiceId); if (service == null) { - logger.debug("Persistence service not found '{}'.", serviceId); - return JSONResponse.createErrorResponse(Status.BAD_REQUEST, "Persistence service not found: " + serviceId); + logger.debug("Persistence service not found '{}'.", effectiveServiceId); + return JSONResponse.createErrorResponse(Status.BAD_REQUEST, + "Persistence service not found: " + effectiveServiceId); } if (!(service instanceof QueryablePersistenceService)) { - logger.debug("Persistence service not queryable '{}'.", serviceId); + logger.debug("Persistence service not queryable '{}'.", effectiveServiceId); return JSONResponse.createErrorResponse(Status.BAD_REQUEST, - "Persistence service not queryable: " + serviceId); + "Persistence service not queryable: " + effectiveServiceId); } QueryablePersistenceService qService = (QueryablePersistenceService) service; - return JSONResponse.createResponse(Status.OK, qService.getItemInfo(), ""); + PersistenceServiceConfiguration config = persistenceServiceConfigurationRegistry.get(effectiveServiceId); + Map aliases = config != null ? config.getAliases() : Map.of(); + Set itemInfo = qService.getItemInfo().stream().map(info -> { + String alias = aliases.get(info.getName()); + if (alias != null) { + return new PersistenceItemInfo() { + + @Override + public String getName() { + return alias; + } + + @Override + public @Nullable Integer getCount() { + return info.getCount(); + } + + @Override + public @Nullable Date getEarliest() { + return info.getEarliest(); + } + + @Override + public @Nullable Date getLatest() { + return info.getLatest(); + } + }; + } else { + return info; + } + }).collect(Collectors.toSet()); + return JSONResponse.createResponse(Status.OK, itemInfo, ""); } private Response deletePersistenceItemData(@Nullable String serviceId, String itemName, @Nullable String timeBegin, @@ -601,13 +646,15 @@ private Response deletePersistenceItemData(@Nullable String serviceId, String it // This is necessary for values that don't change often otherwise data will start after the start of the graph // (or not at all if there's no change during the graph period) FilterCriteria filter = new FilterCriteria(); + PersistenceServiceConfiguration config = persistenceServiceConfigurationRegistry.get(serviceId); + String alias = config != null ? config.getAliases().get(itemName) : null; filter.setItemName(itemName); filter.setBeginDate(dateTimeBegin); filter.setEndDate(dateTimeEnd); ModifiablePersistenceService mService = (ModifiablePersistenceService) service; try { - mService.remove(filter); + mService.remove(filter, alias); } catch (IllegalArgumentException e) { return JSONResponse.createErrorResponse(Status.BAD_REQUEST, "Invalid filter parameters."); } @@ -620,7 +667,7 @@ private Response putItemState(@Nullable String serviceId, String itemName, Strin String effectiveServiceId = serviceId != null ? serviceId : persistenceServiceRegistry.getDefaultId(); PersistenceService service = persistenceServiceRegistry.get(effectiveServiceId); - if (service == null) { + if (effectiveServiceId == null || service == null) { logger.warn("Persistence service not found '{}'.", effectiveServiceId); return JSONResponse.createErrorResponse(Status.BAD_REQUEST, "Persistence service not found: " + effectiveServiceId); @@ -658,7 +705,9 @@ private Response putItemState(@Nullable String serviceId, String itemName, Strin } ModifiablePersistenceService mService = (ModifiablePersistenceService) service; - mService.store(item, dateTime, state); + PersistenceServiceConfiguration config = persistenceServiceConfigurationRegistry.get(effectiveServiceId); + String alias = config != null ? config.getAliases().get(itemName) : null; + mService.store(item, dateTime, state, alias); persistenceManager.handleExternalPersistenceDataChange(mService, item); diff --git a/bundles/org.openhab.core.io.rest.core/src/test/java/org/openhab/core/io/rest/core/internal/persistence/PersistenceResourceTest.java b/bundles/org.openhab.core.io.rest.core/src/test/java/org/openhab/core/io/rest/core/internal/persistence/PersistenceResourceTest.java index d22e6a90e4e..da7fb69b41b 100644 --- a/bundles/org.openhab.core.io.rest.core/src/test/java/org/openhab/core/io/rest/core/internal/persistence/PersistenceResourceTest.java +++ b/bundles/org.openhab.core.io.rest.core/src/test/java/org/openhab/core/io/rest/core/internal/persistence/PersistenceResourceTest.java @@ -17,7 +17,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.collection.IsCollectionWithSize.hasSize; import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import java.time.ZoneId; @@ -58,6 +58,7 @@ * Tests for PersistenceItem Restresource * * @author Stefan Triller - Initial contribution + * @author Mark Herwege - Implement aliases */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) @@ -112,7 +113,7 @@ public String getName() { }); } - when(pServiceMock.query(any())).thenReturn(items); + when(pServiceMock.query(any(), any())).thenReturn(items); when(persistenceServiceRegistryMock.get(PERSISTENCE_SERVICE_ID)).thenReturn(pServiceMock); when(timeZoneProviderMock.getTimeZone()).thenReturn(ZoneId.systemDefault()); diff --git a/bundles/org.openhab.core.model.persistence/src/org/openhab/core/model/persistence/Persistence.xtext b/bundles/org.openhab.core.model.persistence/src/org/openhab/core/model/persistence/Persistence.xtext index a1513a44b70..f46d46c6496 100644 --- a/bundles/org.openhab.core.model.persistence/src/org/openhab/core/model/persistence/Persistence.xtext +++ b/bundles/org.openhab.core.model.persistence/src/org/openhab/core/model/persistence/Persistence.xtext @@ -11,6 +11,7 @@ PersistenceModel: '}' ('Filters' '{' filters+=Filter* '}')? ('Items' '{' configs+=PersistenceConfiguration* '}')? + ('Aliases' '{' aliases+=AliasConfiguration* '}')? ; Strategy: @@ -57,7 +58,7 @@ NotIncludeFilter: PersistenceConfiguration: items+=(AllConfig | ItemConfig | GroupConfig | ItemExcludeConfig | GroupExcludeConfig) - (',' items+=(AllConfig | ItemConfig | GroupConfig | ItemExcludeConfig | GroupExcludeConfig))* ('->' alias=STRING)? + (',' items+=(AllConfig | ItemConfig | GroupConfig | ItemExcludeConfig | GroupExcludeConfig))* ((':' ('strategy' '=' strategies+=[Strategy|ID] (',' strategies+=[Strategy|ID])*)? ('filter' '=' filters+=[Filter|ID] (',' filters+=[Filter|ID])*)?) | ';') @@ -84,6 +85,10 @@ GroupExcludeConfig: '!' groupExclude=ID '*' ; +AliasConfiguration: + item=ID '->' alias=(ID|STRING) +; + DECIMAL returns ecore::EBigDecimal : '-'? INT ('.' INT)? ; diff --git a/bundles/org.openhab.core.model.persistence/src/org/openhab/core/model/persistence/internal/PersistenceModelManager.java b/bundles/org.openhab.core.model.persistence/src/org/openhab/core/model/persistence/internal/PersistenceModelManager.java index 9ee3a8f342c..40ac2980ec0 100644 --- a/bundles/org.openhab.core.model.persistence/src/org/openhab/core/model/persistence/internal/PersistenceModelManager.java +++ b/bundles/org.openhab.core.model.persistence/src/org/openhab/core/model/persistence/internal/PersistenceModelManager.java @@ -13,6 +13,7 @@ package org.openhab.core.model.persistence.internal; import java.util.Collection; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -24,6 +25,7 @@ import org.openhab.core.model.core.EventType; import org.openhab.core.model.core.ModelRepository; import org.openhab.core.model.core.ModelRepositoryChangeListener; +import org.openhab.core.model.persistence.persistence.AliasConfiguration; import org.openhab.core.model.persistence.persistence.AllConfig; import org.openhab.core.model.persistence.persistence.CronStrategy; import org.openhab.core.model.persistence.persistence.EqualsFilter; @@ -71,6 +73,7 @@ * @author Kai Kreuzer - Initial contribution * @author Markus Rathgeb - Move non-model logic to core.persistence * @author Jan N. Klug - Refactored to {@link PersistenceServiceConfigurationProvider} + * @author Mark Herwege - Separate alias handling */ @Component(immediate = true, service = PersistenceServiceConfigurationProvider.class) @NonNullByDefault @@ -112,8 +115,9 @@ public void modelChanged(String modelName, EventType type) { if (model != null) { PersistenceServiceConfiguration newConfiguration = new PersistenceServiceConfiguration(serviceName, - mapConfigs(model.getConfigs()), mapStrategies(model.getDefaults()), - mapStrategies(model.getStrategies()), mapFilters(model.getFilters())); + mapConfigs(model.getConfigs()), mapAliases(model.getAliases()), + mapStrategies(model.getDefaults()), mapStrategies(model.getStrategies()), + mapFilters(model.getFilters())); PersistenceServiceConfiguration oldConfiguration = configurations.put(serviceName, newConfiguration); if (oldConfiguration == null) { @@ -167,10 +171,18 @@ private PersistenceItemConfiguration mapConfig(PersistenceConfiguration config) items.add(new PersistenceItemExcludeConfig(itemExcludeConfig.getItemExclude())); } } - return new PersistenceItemConfiguration(items, config.getAlias(), mapStrategies(config.getStrategies()), + return new PersistenceItemConfiguration(items, mapStrategies(config.getStrategies()), mapFilters(config.getFilters())); } + private Map mapAliases(List aliases) { + final Map map = new HashMap<>(); + for (final AliasConfiguration alias : aliases) { + map.put(alias.getItem(), alias.getAlias()); + } + return map; + } + private List mapStrategies(List strategies) { final List lst = new LinkedList<>(); for (final Strategy strategy : strategies) { diff --git a/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/FilterCriteria.java b/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/FilterCriteria.java index 15f61da8280..5b6cda3acb2 100644 --- a/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/FilterCriteria.java +++ b/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/FilterCriteria.java @@ -35,8 +35,8 @@ * a filter. * * @author Kai Kreuzer - Initial contribution - * @author Lyubomir Papazov - Deprecate methods using java.util and add methods - * that use Java8's ZonedDateTime + * @author Lyubomir Papazov - Deprecate methods using java.util and add methods that use Java8's ZonedDateTime + * @author Mark Herwege - Copy constructor */ @NonNullByDefault public class FilterCriteria { @@ -91,6 +91,20 @@ public enum Ordering { /** Filter result to only contain entries that evaluate to true with the given operator and state */ private @Nullable State state; + public FilterCriteria() { + } + + public FilterCriteria(FilterCriteria filter) { + this.itemName = filter.itemName; + this.beginDate = filter.beginDate; + this.endDate = filter.endDate; + this.pageNumber = filter.pageNumber; + this.pageSize = filter.pageSize; + this.operator = filter.operator; + this.ordering = filter.ordering; + this.state = filter.state; + } + public @Nullable String getItemName() { return itemName; } diff --git a/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/ModifiablePersistenceService.java b/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/ModifiablePersistenceService.java index 5ebee260fab..1ed32db967c 100644 --- a/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/ModifiablePersistenceService.java +++ b/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/ModifiablePersistenceService.java @@ -25,6 +25,7 @@ * and then periodically provide it to the server to be accommodated. * * @author Chris Jackson - Initial contribution + * @author Mark Herwege - Implement aliases */ @NonNullByDefault public interface ModifiablePersistenceService extends QueryablePersistenceService { @@ -70,10 +71,35 @@ public interface ModifiablePersistenceService extends QueryablePersistenceServic * Removes data associated with an item from a persistence service. * If all data is removed for the specified item, the persistence service should free any resources associated with * the item (e.g. remove any tables or delete files from the storage). + * If the persistence service implementing this method supports aliases for item names, the default implementation + * of {@link #remove(FilterCriteria, String)} should be overriden as well. * * @param filter the filter to apply to the data removal. ItemName can not be null. * @return true if the query executed successfully * @throws IllegalArgumentException if item name is null. */ boolean remove(FilterCriteria filter) throws IllegalArgumentException; + + /** + * Removes data associated with an item from a persistence service. + * If all data is removed for the specified item, the persistence service should free any resources associated with + * the item (e.g. remove any tables or delete files from the storage). + * Persistence services supporting aliases, and relying on lookups in the item registry, should override the default + * implementation from this interface. + * + * @param filter the filter to apply to the data removal. ItemName can not be null. + * @param alias for item name in database + * @return true if the query executed successfully + * @throws IllegalArgumentException if item name is null. + */ + default boolean remove(FilterCriteria filter, @Nullable String alias) throws IllegalArgumentException { + // Default implementation changes the filter to have the alias as itemName. + // This gives correct results as long as the persistence service does not rely on a lookup in the item registry + // (in which case the item will not be found). + if (alias != null) { + FilterCriteria aliasFilter = new FilterCriteria(filter).setItemName(alias); + return remove(aliasFilter); + } + return remove(filter); + } } diff --git a/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/PersistenceItemConfiguration.java b/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/PersistenceItemConfiguration.java index b24fa553473..43e6fb02def 100644 --- a/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/PersistenceItemConfiguration.java +++ b/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/PersistenceItemConfiguration.java @@ -25,15 +25,15 @@ * This class holds the configuration of a persistence strategy for specific items. * * @author Markus Rathgeb - Initial contribution + * @author Mark Herwege - Extract alias configuration */ @NonNullByDefault -public record PersistenceItemConfiguration(List items, @Nullable String alias, - List strategies, List filters) { +public record PersistenceItemConfiguration(List items, List strategies, + List filters) { - public PersistenceItemConfiguration(final List items, @Nullable final String alias, + public PersistenceItemConfiguration(final List items, @Nullable final List strategies, @Nullable final List filters) { this.items = items; - this.alias = alias; this.strategies = Objects.requireNonNullElse(strategies, List.of()); this.filters = Objects.requireNonNullElse(filters, List.of()); } diff --git a/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/QueryablePersistenceService.java b/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/QueryablePersistenceService.java index 8fb63e31f2c..53f5a24e42e 100644 --- a/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/QueryablePersistenceService.java +++ b/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/QueryablePersistenceService.java @@ -15,6 +15,8 @@ import java.time.ZonedDateTime; import java.util.Iterator; import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -29,23 +31,68 @@ * @author Kai Kreuzer - Initial contribution * @author Chris Jackson - Added getItems method * @author Mark Herwege - Added methods to retrieve lastUpdate, lastChange and lastState from persistence + * @author Mark Herwege - Implement aliases */ @NonNullByDefault public interface QueryablePersistenceService extends PersistenceService { /** * Queries the {@link PersistenceService} for historic data with a given {@link FilterCriteria}. + * If the persistence service implementing this class supports using aliases for item names, the default + * implementation of {@link #query(FilterCriteria, String)} should be overridden as well. * * @param filter the filter to apply to the query * @return a time series of items */ Iterable query(FilterCriteria filter); + /** + * Queries the {@link PersistenceService} for historic data with a given {@link FilterCriteria}. + * If the persistence service implementing this interface supports aliases and relies on item registry lookups, the + * default implementation should be overridden to query the database with the aliased name. + * + * @param filter the filter to apply to the query + * @param alias for item name in database + * @return a time series of items + */ + default Iterable query(FilterCriteria filter, @Nullable String alias) { + // Default implementation changes the filter to have the alias as itemName and sets it back in the returned + // result. + // This gives correct results as long as the persistence service does not rely on a lookup in the item registry + // (in which case the item will not be found). + String itemName = filter.getItemName(); + if (itemName != null && alias != null) { + FilterCriteria aliasFilter = new FilterCriteria(filter).setItemName(alias); + return StreamSupport.stream(query(aliasFilter).spliterator(), false).map(hi -> new HistoricItem() { + + @Override + public ZonedDateTime getTimestamp() { + return hi.getTimestamp(); + } + + @Override + public State getState() { + return hi.getState(); + } + + @Override + public String getName() { + return itemName; + } + }).collect(Collectors.toList()); + } + return query(filter); + } + /** * Returns a set of {@link PersistenceItemInfo} about items that are stored in the persistence service. This allows * the persistence service to return information about items that are no long available as an - * {@link org.openhab.core.items.Item} in - * openHAB. If it is not possible to retrieve the information an empty set should be returned. + * {@link org.openhab.core.items.Item} in openHAB. If it is not possible to retrieve the information an empty set + * should be returned. + * + * Note that implementations for method callers that this method may return an alias for an existing item if the + * database does not store the mapping between item name and alias or the reverse mapping is not implemented in the + * persistence service. * * @return a set of information about the persisted items */ @@ -58,9 +105,12 @@ public interface QueryablePersistenceService extends PersistenceService { * persisted state. Persistence services can override this default implementation with a more specific or efficient * algorithm. * + * @param itemName name of item + * @param alias alias of item + * * @return a {@link PersistedItem} or null if the item has not been persisted */ - default @Nullable PersistedItem persistedItem(String itemName) { + default @Nullable PersistedItem persistedItem(String itemName, @Nullable String alias) { State currentState = UnDefType.NULL; State previousState = null; ZonedDateTime lastUpdate = null; @@ -69,7 +119,7 @@ public interface QueryablePersistenceService extends PersistenceService { int pageNumber = 0; FilterCriteria filter = new FilterCriteria().setItemName(itemName).setEndDate(ZonedDateTime.now()) .setOrdering(Ordering.DESCENDING).setPageSize(1000).setPageNumber(pageNumber); - Iterable items = query(filter); + Iterable items = query(filter, alias); while (items != null) { Iterator it = items.iterator(); int itemCount = 0; diff --git a/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/dto/PersistenceItemConfigurationDTO.java b/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/dto/PersistenceItemConfigurationDTO.java index 2cbe267eccb..8e956ed778f 100644 --- a/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/dto/PersistenceItemConfigurationDTO.java +++ b/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/dto/PersistenceItemConfigurationDTO.java @@ -16,7 +16,6 @@ import java.util.List; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; /** * The {@link org.openhab.core.persistence.dto.PersistenceItemConfigurationDTO} is used for transferring persistence @@ -29,5 +28,4 @@ public class PersistenceItemConfigurationDTO { public Collection items = List.of(); public Collection strategies = List.of(); public Collection filters = List.of(); - public @Nullable String alias; } diff --git a/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/dto/PersistenceServiceConfigurationDTO.java b/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/dto/PersistenceServiceConfigurationDTO.java index 3460b0583a5..001f75ef549 100644 --- a/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/dto/PersistenceServiceConfigurationDTO.java +++ b/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/dto/PersistenceServiceConfigurationDTO.java @@ -14,6 +14,7 @@ import java.util.Collection; import java.util.List; +import java.util.Map; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -26,6 +27,7 @@ public class PersistenceServiceConfigurationDTO { public String serviceId = ""; public Collection configs = List.of(); + public Map aliases = Map.of(); public Collection defaults = List.of(); public Collection cronStrategies = List.of(); public Collection thresholdFilters = List.of(); diff --git a/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/extensions/PersistenceExtensions.java b/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/extensions/PersistenceExtensions.java index 1fc56661a3c..e4286238b82 100644 --- a/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/extensions/PersistenceExtensions.java +++ b/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/extensions/PersistenceExtensions.java @@ -41,6 +41,8 @@ import org.openhab.core.persistence.PersistenceService; import org.openhab.core.persistence.PersistenceServiceRegistry; import org.openhab.core.persistence.QueryablePersistenceService; +import org.openhab.core.persistence.registry.PersistenceServiceConfiguration; +import org.openhab.core.persistence.registry.PersistenceServiceConfigurationRegistry; import org.openhab.core.types.State; import org.openhab.core.types.TimeSeries; import org.openhab.core.types.TypeParser; @@ -73,12 +75,15 @@ public class PersistenceExtensions { private static @Nullable PersistenceServiceRegistry registry; + private static @Nullable PersistenceServiceConfigurationRegistry configRegistry; private static @Nullable TimeZoneProvider timeZoneProvider; @Activate public PersistenceExtensions(@Reference PersistenceServiceRegistry registry, + @Reference PersistenceServiceConfigurationRegistry configRegistry, @Reference TimeZoneProvider timeZoneProvider) { PersistenceExtensions.registry = registry; + PersistenceExtensions.configRegistry = configRegistry; PersistenceExtensions.timeZoneProvider = timeZoneProvider; } @@ -109,7 +114,7 @@ private static void internalPersist(Item item, @Nullable String serviceId) { } PersistenceService service = getService(effectiveServiceId); if (service != null) { - service.store(item); + service.store(item, getAlias(item, effectiveServiceId)); return; } LoggerFactory.getLogger(PersistenceExtensions.class) @@ -148,7 +153,7 @@ private static void internalPersist(Item item, ZonedDateTime timestamp, State st } PersistenceService service = getService(effectiveServiceId); if (service instanceof ModifiablePersistenceService modifiableService) { - modifiableService.store(item, timestamp, state); + modifiableService.store(item, timestamp, state, getAlias(item, effectiveServiceId)); return; } LoggerFactory.getLogger(PersistenceExtensions.class) @@ -226,8 +231,9 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable internalRemoveAllStatesBetween(item, timeSeries.getBegin().atZone(timeZone), timeSeries.getEnd().atZone(timeZone), serviceId); } + String alias = getAlias(item, effectiveServiceId); timeSeries.getStates() - .forEach(s -> modifiableService.store(item, s.timestamp().atZone(timeZone), s.state())); + .forEach(s -> modifiableService.store(item, s.timestamp().atZone(timeZone), s.state(), alias)); return; } LoggerFactory.getLogger(PersistenceExtensions.class) @@ -316,10 +322,11 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable if (service instanceof QueryablePersistenceService qService) { FilterCriteria filter = new FilterCriteria(); filter.setEndDate(timestamp); + String alias = getAlias(item, effectiveServiceId); filter.setItemName(item.getName()); filter.setPageSize(1); filter.setOrdering(Ordering.DESCENDING); - Iterable result = qService.query(filter); + Iterable result = qService.query(filter, alias); if (result.iterator().hasNext()) { return result.iterator().next(); } @@ -453,6 +460,7 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable PersistenceService service = getService(effectiveServiceId); if (service instanceof QueryablePersistenceService qService) { FilterCriteria filter = new FilterCriteria(); + String alias = getAlias(item, effectiveServiceId); filter.setItemName(item.getName()); if (forward) { filter.setBeginDate(ZonedDateTime.now()); @@ -465,7 +473,7 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable int startPage = 0; filter.setPageNumber(startPage); - Iterable items = qService.query(filter); + Iterable items = qService.query(filter, alias); while (items != null) { Iterator itemIterator = items.iterator(); int itemCount = 0; @@ -496,7 +504,7 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable } if (itemCount == filter.getPageSize()) { filter.setPageNumber(++startPage); - items = qService.query(filter); + items = qService.query(filter, alias); } else { items = null; } @@ -625,6 +633,7 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable PersistenceService service = getService(effectiveServiceId); if (service instanceof QueryablePersistenceService qService) { FilterCriteria filter = new FilterCriteria(); + String alias = getAlias(item, effectiveServiceId); filter.setItemName(item.getName()); if (forward) { filter.setBeginDate(ZonedDateTime.now()); @@ -637,7 +646,7 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable int startPage = 0; filter.setPageNumber(startPage); - Iterable items = qService.query(filter); + Iterable items = qService.query(filter, alias); while (items != null) { Iterator itemIterator = items.iterator(); int itemCount = 0; @@ -650,7 +659,7 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable } if (itemCount == filter.getPageSize()) { filter.setPageNumber(++startPage); - items = qService.query(filter); + items = qService.query(filter, alias); } else { items = null; } @@ -2547,10 +2556,11 @@ private static void internalPersist(Item item, TimeSeries timeSeries, @Nullable } else { filter.setEndDate(ZonedDateTime.now()); } + String alias = getAlias(item, effectiveServiceId); filter.setItemName(item.getName()); filter.setOrdering(Ordering.ASCENDING); - return qService.query(filter); + return qService.query(filter, alias); } else { LoggerFactory.getLogger(PersistenceExtensions.class) .warn("There is no queryable persistence service registered with the id '{}'", effectiveServiceId); @@ -2663,10 +2673,11 @@ private static void internalRemoveAllStatesBetween(Item item, @Nullable ZonedDat } else { filter.setEndDate(ZonedDateTime.now()); } + String alias = getAlias(item, effectiveServiceId); filter.setItemName(item.getName()); filter.setOrdering(Ordering.ASCENDING); - mService.remove(filter); + mService.remove(filter, alias); } else { LoggerFactory.getLogger(PersistenceExtensions.class) .warn("There is no modifiable persistence service registered with the id '{}'", effectiveServiceId); @@ -2743,6 +2754,15 @@ private static void internalRemoveAllStatesBetween(Item item, @Nullable ZonedDat return null; } + private static @Nullable String getAlias(Item item, String serviceId) { + PersistenceServiceConfigurationRegistry reg = configRegistry; + if (reg != null) { + PersistenceServiceConfiguration config = reg.get(serviceId); + return config != null ? config.getAliases().get(item.getName()) : null; + } + return null; + } + private static @Nullable DecimalType getItemValue(Item item) { Item baseItem = item instanceof GroupItem groupItem ? groupItem.getBaseItem() : item; if (baseItem instanceof NumberItem numberItem) { diff --git a/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/internal/PersistenceManagerImpl.java b/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/internal/PersistenceManagerImpl.java index 653ef2577c1..81f127fc6f0 100644 --- a/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/internal/PersistenceManagerImpl.java +++ b/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/internal/PersistenceManagerImpl.java @@ -188,7 +188,7 @@ private void handleStateEvent(Item item, boolean changed) { .filter(itemConfig -> itemConfig.filters().stream().allMatch(filter -> filter.apply(item))) .forEach(itemConfig -> { itemConfig.filters().forEach(filter -> filter.persisted(item)); - container.getPersistenceService().store(item, itemConfig.alias()); + container.getPersistenceService().store(item, container.getAlias(item)); })); } @@ -353,7 +353,7 @@ public void timeSeriesUpdated(Item item, TimeSeries timeSeries) { ZonedDateTime end = timeSeries.getEnd().atZone(ZoneId.systemDefault()); FilterCriteria removeFilter = new FilterCriteria().setItemName(item.getName()) .setBeginDate(begin).setEndDate(end); - service.remove(removeFilter); + service.remove(removeFilter, container.getAlias(item)); ScheduledCompletableFuture forecastJob = container.forecastJobs.get(item.getName()); if (forecastJob != null && forecastJob.getScheduledTime().isAfter(begin) && forecastJob.getScheduledTime().isBefore(end)) { @@ -478,12 +478,17 @@ public Stream getMatchingConfigurations(Persistenc }).stream()); } + public @Nullable String getAlias(Item item) { + return configuration.getAliases().get(item.getName()); + } + private PersistenceServiceConfiguration getDefaultConfig() { List strategies = persistenceService.getDefaultStrategies(); List configs = List - .of(new PersistenceItemConfiguration(List.of(new PersistenceAllConfig()), null, strategies, null)); - return new PersistenceServiceConfiguration(persistenceService.getId(), configs, strategies, strategies, - List.of()); + .of(new PersistenceItemConfiguration(List.of(new PersistenceAllConfig()), strategies, null)); + Map aliases = Map.of(); + return new PersistenceServiceConfiguration(persistenceService.getId(), configs, aliases, strategies, + strategies, List.of()); } /** @@ -550,6 +555,7 @@ public void removeItem(String itemName) { private void restoreItemStateIfPossible(Item item) { QueryablePersistenceService queryService = (QueryablePersistenceService) persistenceService; + String alias = getAlias(item); PersistedItem persistedItem = safeCaller.create(queryService, QueryablePersistenceService.class) .onTimeout( @@ -558,8 +564,9 @@ private void restoreItemStateIfPossible(Item item) { .onException(e -> logger.error( "Exception occurred while querying persistence service '{}' to restore '{}': {}", queryService.getId(), item.getName(), e.getMessage(), e)) - .build().persistedItem(item.getName()); + .build().persistedItem(item.getName(), alias); if (persistedItem == null) { + // in case of an exception or timeout, the safe caller returns null return; } GenericItem genericItem = (GenericItem) item; @@ -590,6 +597,7 @@ public void scheduleNextForecastForItem(String itemName, Instant time, State sta public void scheduleNextPersistedForecastForItem(String itemName) { Item item = itemRegistry.get(itemName); if (item instanceof GenericItem) { + String alias = getAlias(item); QueryablePersistenceService queryService = (QueryablePersistenceService) persistenceService; FilterCriteria filter = new FilterCriteria().setItemName(itemName).setBeginDate(ZonedDateTime.now()) .setOrdering(ASCENDING); @@ -598,7 +606,7 @@ public void scheduleNextPersistedForecastForItem(String itemName) { queryService.getId(), SafeCaller.DEFAULT_TIMEOUT)) .onException(e -> logger.error("Exception occurred while querying persistence service '{}': {}", queryService.getId(), e.getMessage(), e)) - .build().query(filter).iterator(); + .build().query(filter, alias).iterator(); while (result.hasNext()) { HistoricItem next = result.next(); Instant timestamp = next.getInstant(); @@ -624,7 +632,7 @@ private void persistJob(List itemConfigs) { if (itemConfig.filters().stream().allMatch(filter -> filter.apply(item))) { long startTime = System.nanoTime(); itemConfig.filters().forEach(filter -> filter.persisted(item)); - persistenceService.store(item, itemConfig.alias()); + persistenceService.store(item, getAlias(item)); logger.trace("Storing item '{}' with persistence service '{}' took {}ms", item.getName(), configuration.getUID(), TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime)); } diff --git a/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/registry/PersistenceServiceConfiguration.java b/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/registry/PersistenceServiceConfiguration.java index a714a7f2256..90be4693812 100644 --- a/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/registry/PersistenceServiceConfiguration.java +++ b/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/registry/PersistenceServiceConfiguration.java @@ -14,6 +14,7 @@ import java.util.Collection; import java.util.List; +import java.util.Map; import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.core.common.registry.Identifiable; @@ -25,20 +26,23 @@ * The {@link PersistenceServiceConfiguration} represents the configuration for a persistence service. * * @author Jan N. Klug - Initial contribution + * @author Mark Herwege - Implement aliases */ @NonNullByDefault public class PersistenceServiceConfiguration implements Identifiable { private final String serviceId; private final List configs; + private final Map aliases; private final List defaults; private final List strategies; private final List filters; public PersistenceServiceConfiguration(String serviceId, Collection configs, - Collection defaults, Collection strategies, - Collection filters) { + Map aliases, Collection defaults, + Collection strategies, Collection filters) { this.serviceId = serviceId; this.configs = List.copyOf(configs); + this.aliases = Map.copyOf(aliases); this.defaults = List.copyOf(defaults); this.strategies = List.copyOf(strategies); this.filters = List.copyOf(filters); @@ -58,6 +62,15 @@ public List getConfigs() { return configs; } + /** + * Get the item aliases. + * + * @return a map of items to aliases + */ + public Map getAliases() { + return aliases; + } + /** * Get the default strategies. * diff --git a/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/registry/PersistenceServiceConfigurationDTOMapper.java b/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/registry/PersistenceServiceConfigurationDTOMapper.java index 173bde11c0d..a008b8af66f 100644 --- a/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/registry/PersistenceServiceConfigurationDTOMapper.java +++ b/bundles/org.openhab.core.persistence/src/main/java/org/openhab/core/persistence/registry/PersistenceServiceConfigurationDTOMapper.java @@ -46,6 +46,7 @@ * The {@link PersistenceServiceConfigurationDTOMapper} is a utility class to map persistence configurations for storage * * @author Jan N. Klug - Initial contribution + * @author Mark Herwege - Implement aliases */ @NonNullByDefault public class PersistenceServiceConfigurationDTOMapper { @@ -60,6 +61,7 @@ public static PersistenceServiceConfigurationDTO map( dto.serviceId = persistenceServiceConfiguration.getUID(); dto.configs = persistenceServiceConfiguration.getConfigs().stream() .map(PersistenceServiceConfigurationDTOMapper::mapPersistenceItemConfig).toList(); + dto.aliases = Map.copyOf(persistenceServiceConfiguration.getAliases()); dto.defaults = persistenceServiceConfiguration.getDefaults().stream().map(PersistenceStrategy::getName) .toList(); dto.cronStrategies = filterList(persistenceServiceConfiguration.getStrategies(), PersistenceCronStrategy.class, @@ -100,10 +102,12 @@ public static PersistenceServiceConfiguration map(PersistenceServiceConfiguratio .map(str -> stringToPersistenceStrategy(str, strategyMap, dto.serviceId)).toList(); List filters = config.filters.stream() .map(str -> stringToPersistenceFilter(str, filterMap, dto.serviceId)).toList(); - return new PersistenceItemConfiguration(items, config.alias, strategies, filters); + return new PersistenceItemConfiguration(items, strategies, filters); }).toList(); - return new PersistenceServiceConfiguration(dto.serviceId, configs, defaults, strategyMap.values(), + Map aliases = Map.copyOf(dto.aliases); + + return new PersistenceServiceConfiguration(dto.serviceId, configs, aliases, defaults, strategyMap.values(), filterMap.values()); } @@ -171,7 +175,6 @@ private static PersistenceItemConfigurationDTO mapPersistenceItemConfig(Persiste .toList(); itemDto.strategies = config.strategies().stream().map(PersistenceStrategy::getName).toList(); itemDto.filters = config.filters().stream().map(PersistenceFilter::getName).toList(); - itemDto.alias = config.alias(); return itemDto; } diff --git a/bundles/org.openhab.core.persistence/src/test/java/org/openhab/core/persistence/extensions/PersistenceExtensionsTest.java b/bundles/org.openhab.core.persistence/src/test/java/org/openhab/core/persistence/extensions/PersistenceExtensionsTest.java index b7a6df2880e..ecf1745efdc 100644 --- a/bundles/org.openhab.core.persistence/src/test/java/org/openhab/core/persistence/extensions/PersistenceExtensionsTest.java +++ b/bundles/org.openhab.core.persistence/src/test/java/org/openhab/core/persistence/extensions/PersistenceExtensionsTest.java @@ -16,6 +16,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.closeTo; import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.when; import static org.openhab.core.persistence.extensions.TestPersistenceService.*; @@ -52,6 +53,7 @@ import org.openhab.core.persistence.HistoricItem; import org.openhab.core.persistence.PersistenceService; import org.openhab.core.persistence.PersistenceServiceRegistry; +import org.openhab.core.persistence.registry.PersistenceServiceConfigurationRegistry; import org.openhab.core.types.State; /** @@ -64,6 +66,7 @@ * @author Mark Herwege - lastChange and nextChange methods * @author Mark Herwege - handle persisted GroupItem with QuantityType * @author Mark Herwege - add median methods + * @author Mark Herwege - Implement aliases */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) @@ -81,6 +84,8 @@ public class PersistenceExtensionsTest { private @Mock @NonNullByDefault({}) UnitProvider unitProviderMock; private @Mock @NonNullByDefault({}) TimeZoneProvider timeZoneProviderMock; + private @Mock @NonNullByDefault({}) PersistenceServiceConfigurationRegistry persistenceServiceConfigurationRegistryMock; + private @NonNullByDefault({}) GenericItem numberItem, quantityItem, groupQuantityItem, switchItem; @BeforeEach @@ -109,6 +114,7 @@ public void setUp() { when(itemRegistryMock.get(TEST_SWITCH)).thenReturn(switchItem); when(itemRegistryMock.get(TEST_GROUP_QUANTITY_NUMBER)).thenReturn(groupQuantityItem); + when(persistenceServiceConfigurationRegistryMock.get(anyString())).thenReturn(null); when(timeZoneProviderMock.getTimeZone()).thenReturn(ZoneId.systemDefault()); new PersistenceExtensions(new PersistenceServiceRegistry() { @@ -135,7 +141,7 @@ public Set getAll() { public @Nullable PersistenceService get(@Nullable String serviceId) { return TestPersistenceService.SERVICE_ID.equals(serviceId) ? testPersistenceService : null; } - }, timeZoneProviderMock); + }, persistenceServiceConfigurationRegistryMock, timeZoneProviderMock); } @Test @@ -3397,7 +3403,7 @@ public Set getAll() { public @Nullable PersistenceService get(@Nullable String serviceId) { return TestCachedValuesPersistenceService.ID.equals(serviceId) ? persistenceService : null; } - }, timeZoneProviderMock); + }, persistenceServiceConfigurationRegistryMock, timeZoneProviderMock); if (historicHours > 0) { ZonedDateTime beginHistory = now.minusHours(historicHours); diff --git a/bundles/org.openhab.core.persistence/src/test/java/org/openhab/core/persistence/internal/PersistenceManagerTest.java b/bundles/org.openhab.core.persistence/src/test/java/org/openhab/core/persistence/internal/PersistenceManagerTest.java index b8040df59b4..acc982c8975 100644 --- a/bundles/org.openhab.core.persistence/src/test/java/org/openhab/core/persistence/internal/PersistenceManagerTest.java +++ b/bundles/org.openhab.core.persistence/src/test/java/org/openhab/core/persistence/internal/PersistenceManagerTest.java @@ -86,6 +86,7 @@ * The {@link PersistenceManagerTest} contains tests for the {@link PersistenceManagerImpl} * * @author Jan N. Klug - Initial contribution + * @author Mark Herwege - Implement aliases */ @NonNullByDefault @ExtendWith(MockitoExtension.class) @@ -185,8 +186,8 @@ public void setUp() throws ItemNotFoundException { when(itemRegistryMock.getItems()).thenReturn(List.of(TEST_ITEM, TEST_ITEM2, TEST_ITEM3, TEST_GROUP_ITEM)); when(persistenceServiceMock.getId()).thenReturn(TEST_PERSISTENCE_SERVICE_ID); when(queryablePersistenceServiceMock.getId()).thenReturn(TEST_QUERYABLE_PERSISTENCE_SERVICE_ID); - when(queryablePersistenceServiceMock.query(any())).thenReturn(List.of(TEST_HISTORIC_ITEM)); - when(queryablePersistenceServiceMock.persistedItem(any())).thenReturn(TEST_PERSISTED_ITEM); + when(queryablePersistenceServiceMock.query(any(), any())).thenReturn(List.of(TEST_HISTORIC_ITEM)); + when(queryablePersistenceServiceMock.persistedItem(any(), any())).thenReturn(TEST_PERSISTED_ITEM); when(modifiablePersistenceServiceMock.getId()).thenReturn(TEST_MODIFIABLE_PERSISTENCE_SERVICE_ID); manager = new PersistenceManagerImpl(cronSchedulerMock, schedulerMock, itemRegistryMock, safeCallerMock, @@ -367,7 +368,7 @@ public void restoreOnStartupWhenItemNull() { assertThat(TEST_GROUP_ITEM.getState(), is(TEST_STATE)); assertThat(TEST_GROUP_ITEM.getLastState(), is(TEST_LAST_STATE)); - verify(queryablePersistenceServiceMock, times(3)).persistedItem(any()); + verify(queryablePersistenceServiceMock, times(3)).persistedItem(any(), any()); ZonedDateTime lastStateUpdate = TEST_ITEM.getLastStateUpdate(); assertNotNull(lastStateUpdate); @@ -398,7 +399,7 @@ public void noRestoreOnStartupWhenItemNotNull() { assertThat(TEST_GROUP_ITEM.getState(), is(TEST_STATE)); assertThat(TEST_GROUP_ITEM.getLastState(), is(TEST_LAST_STATE)); - verify(queryablePersistenceServiceMock, times(2)).persistedItem(any()); + verify(queryablePersistenceServiceMock, times(2)).persistedItem(any(), any()); ZonedDateTime lastStateUpdate = TEST_ITEM.getLastStateUpdate(); assertNotNull(lastStateUpdate); @@ -578,7 +579,7 @@ private PersistenceServiceConfiguration addConfiguration(String serviceId, List< PersistenceStrategy strategy, @Nullable PersistenceFilter filter) { List filters = filter != null ? List.of(filter) : List.of(); - PersistenceItemConfiguration itemConfiguration = new PersistenceItemConfiguration(itemConfigs, null, + PersistenceItemConfiguration itemConfiguration = new PersistenceItemConfiguration(itemConfigs, List.of(strategy), filters); List strategies = PersistenceStrategy.Globals.STRATEGIES.containsValue(strategy) @@ -586,7 +587,7 @@ private PersistenceServiceConfiguration addConfiguration(String serviceId, List< : List.of(strategy); PersistenceServiceConfiguration serviceConfiguration = new PersistenceServiceConfiguration(serviceId, - List.of(itemConfiguration), List.of(), strategies, filters); + List.of(itemConfiguration), Map.of(), List.of(), strategies, filters); manager.added(serviceConfiguration); return serviceConfiguration; diff --git a/bundles/org.openhab.core.ui/src/main/java/org/openhab/core/ui/internal/chart/defaultchartprovider/DefaultChartProvider.java b/bundles/org.openhab.core.ui/src/main/java/org/openhab/core/ui/internal/chart/defaultchartprovider/DefaultChartProvider.java index 8208cfb1e48..881cfe582d7 100644 --- a/bundles/org.openhab.core.ui/src/main/java/org/openhab/core/ui/internal/chart/defaultchartprovider/DefaultChartProvider.java +++ b/bundles/org.openhab.core.ui/src/main/java/org/openhab/core/ui/internal/chart/defaultchartprovider/DefaultChartProvider.java @@ -49,6 +49,8 @@ import org.openhab.core.persistence.PersistenceService; import org.openhab.core.persistence.PersistenceServiceRegistry; import org.openhab.core.persistence.QueryablePersistenceService; +import org.openhab.core.persistence.registry.PersistenceServiceConfiguration; +import org.openhab.core.persistence.registry.PersistenceServiceConfigurationRegistry; import org.openhab.core.types.State; import org.openhab.core.ui.chart.ChartProvider; import org.openhab.core.ui.internal.chart.ChartServlet; @@ -68,6 +70,7 @@ * @author Holger Reichert - Support for themes, DPI, legend hiding * @author Christoph Weitkamp - Consider default persistence service * @author Jan N. Klug - Add y-axis label formatter + * @author Mark Herwege - Implement aliases */ @NonNullByDefault @Component(immediate = true) @@ -110,12 +113,15 @@ private LegendPosition getLegendPosition() { private final ItemUIRegistry itemUIRegistry; private final PersistenceServiceRegistry persistenceServiceRegistry; + private final PersistenceServiceConfigurationRegistry persistenceServiceConfigurationRegistry; @Activate public DefaultChartProvider(final @Reference ItemUIRegistry itemUIRegistry, - final @Reference PersistenceServiceRegistry persistenceServiceRegistry) { + final @Reference PersistenceServiceRegistry persistenceServiceRegistry, + final @Reference PersistenceServiceConfigurationRegistry persistenceServiceConfigurationRegistry) { this.itemUIRegistry = itemUIRegistry; this.persistenceServiceRegistry = persistenceServiceRegistry; + this.persistenceServiceConfigurationRegistry = persistenceServiceConfigurationRegistry; if (logger.isDebugEnabled()) { logger.debug("Available themes for default chart provider: {}", String.join(", ", CHART_THEMES.keySet())); @@ -188,8 +194,8 @@ public BufferedImage createChart(@Nullable String serviceId, @Nullable String th // axis styler.setAxisTickLabelsFont(chartTheme.getAxisTickLabelsFont(dpi)); styler.setAxisTickLabelsColor(chartTheme.getAxisTickLabelsColor()); - styler.setXAxisMin((double) startTime.toInstant().toEpochMilli()); - styler.setXAxisMax((double) endTime.toInstant().toEpochMilli()); + styler.setXAxisMin(startTime.toInstant().toEpochMilli()); + styler.setXAxisMax(endTime.toInstant().toEpochMilli()); int yAxisSpacing = Math.max(height / 10, chartTheme.getAxisTickLabelsFont(dpi).getSize()); if (yAxisDecimalPattern != null) { styler.setYAxisDecimalPattern(yAxisDecimalPattern); @@ -333,10 +339,13 @@ private boolean addItem(XYChart chart, QueryablePersistenceService service, Zone // after the start of the graph (or not at all if there's no change during the graph period) filter = new FilterCriteria(); filter.setEndDate(timeBegin); - filter.setItemName(item.getName()); + String itemName = item.getName(); + PersistenceServiceConfiguration config = persistenceServiceConfigurationRegistry.get(service.getId()); + String alias = config != null ? config.getAliases().get(itemName) : null; + filter.setItemName(itemName); filter.setPageSize(1); filter.setOrdering(Ordering.DESCENDING); - result = service.query(filter); + result = service.query(filter, alias); if (result.iterator().hasNext()) { HistoricItem historicItem = result.iterator().next(); @@ -352,7 +361,7 @@ private boolean addItem(XYChart chart, QueryablePersistenceService service, Zone filter.setOrdering(Ordering.ASCENDING); // Get the data from the persistence store - result = service.query(filter); + result = service.query(filter, alias); // Iterate through the data for (HistoricItem historicItem : result) {