2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.systeminfo.internal;
15 import static org.openhab.binding.systeminfo.internal.SysteminfoBindingConstants.*;
18 import java.util.Collections;
19 import java.util.HashMap;
20 import java.util.List;
22 import java.util.stream.Collectors;
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.openhab.core.config.core.Configuration;
27 import org.openhab.core.storage.StorageService;
28 import org.openhab.core.thing.Channel;
29 import org.openhab.core.thing.ChannelUID;
30 import org.openhab.core.thing.Thing;
31 import org.openhab.core.thing.ThingTypeUID;
32 import org.openhab.core.thing.ThingUID;
33 import org.openhab.core.thing.binding.AbstractStorageBasedTypeProvider;
34 import org.openhab.core.thing.binding.ThingTypeProvider;
35 import org.openhab.core.thing.binding.builder.ChannelBuilder;
36 import org.openhab.core.thing.type.ChannelGroupDefinition;
37 import org.openhab.core.thing.type.ChannelGroupType;
38 import org.openhab.core.thing.type.ChannelGroupTypeRegistry;
39 import org.openhab.core.thing.type.ChannelGroupTypeUID;
40 import org.openhab.core.thing.type.ChannelType;
41 import org.openhab.core.thing.type.ChannelTypeRegistry;
42 import org.openhab.core.thing.type.ChannelTypeUID;
43 import org.openhab.core.thing.type.ThingType;
44 import org.openhab.core.thing.type.ThingTypeBuilder;
45 import org.openhab.core.thing.type.ThingTypeRegistry;
46 import org.osgi.service.component.annotations.Activate;
47 import org.osgi.service.component.annotations.Component;
48 import org.osgi.service.component.annotations.Reference;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
53 * Extended channels can be auto discovered and added to newly created groups in the
54 * {@link org.openhab.binding.systeminfo.internal.handler.SysteminfoHandler}. The
55 * thing needs to be updated to add the groups. The `SysteminfoThingTypeProvider` OSGi service gives access to the
56 * `ThingTypeRegistry` and serves the updated `ThingType`.
58 * @author Mark Herwege - Initial contribution
62 @Component(service = { SysteminfoThingTypeProvider.class, ThingTypeProvider.class })
63 public class SysteminfoThingTypeProvider extends AbstractStorageBasedTypeProvider {
64 private final Logger logger = LoggerFactory.getLogger(SysteminfoThingTypeProvider.class);
66 private final ThingTypeRegistry thingTypeRegistry;
67 private final ChannelGroupTypeRegistry channelGroupTypeRegistry;
68 private final ChannelTypeRegistry channelTypeRegistry;
70 private final Map<ThingUID, Map<String, Configuration>> thingChannelsConfig = new HashMap<>();
73 public SysteminfoThingTypeProvider(@Reference ThingTypeRegistry thingTypeRegistry,
74 @Reference ChannelGroupTypeRegistry channelGroupTypeRegistry,
75 @Reference ChannelTypeRegistry channelTypeRegistry, @Reference StorageService storageService) {
76 super(storageService);
77 this.thingTypeRegistry = thingTypeRegistry;
78 this.channelGroupTypeRegistry = channelGroupTypeRegistry;
79 this.channelTypeRegistry = channelTypeRegistry;
83 * Create thing type with the provided typeUID and add it to the thing type registry.
86 * @return false if base type UID `systeminfo:computer` cannot be found in the thingTypeRegistry
88 public boolean createThingType(ThingTypeUID typeUID) {
89 logger.trace("Creating thing type {}", typeUID);
90 return updateThingType(typeUID, getChannelGroupDefinitions(typeUID));
94 * Update `ThingType`with `typeUID`, replacing the channel group definitions with `groupDefs`.
98 * @return false if `typeUID` or its base type UID `systeminfo:computer` cannot be found in the thingTypeRegistry
100 public boolean updateThingType(ThingTypeUID typeUID, List<ChannelGroupDefinition> groupDefs) {
101 ThingType baseType = thingTypeRegistry.getThingType(typeUID);
102 if (baseType == null) {
103 baseType = thingTypeRegistry.getThingType(THING_TYPE_COMPUTER);
104 if (baseType == null) {
105 logger.warn("Could not find base thing type in registry.");
109 ThingTypeBuilder builder = createThingTypeBuilder(typeUID, baseType.getUID());
110 if (builder != null) {
111 logger.trace("Adding channel group definitions to thing type");
112 ThingType type = builder.withChannelGroupDefinitions(groupDefs).build();
117 logger.debug("Error adding channel groups");
123 * Return a {@link ThingTypeBuilder} that can create an exact copy of the `ThingType` with `baseTypeUID`.
124 * Further build steps can be performed on the returned object before recreating the `ThingType` from the builder.
128 * @return the ThingTypeBuilder, null if `baseTypeUID` cannot be found in the thingTypeRegistry
130 private @Nullable ThingTypeBuilder createThingTypeBuilder(ThingTypeUID newTypeUID, ThingTypeUID baseTypeUID) {
131 ThingType type = thingTypeRegistry.getThingType(baseTypeUID);
137 ThingTypeBuilder result = ThingTypeBuilder.instance(newTypeUID, type.getLabel())
138 .withChannelGroupDefinitions(type.getChannelGroupDefinitions())
139 .withChannelDefinitions(type.getChannelDefinitions())
140 .withExtensibleChannelTypeIds(type.getExtensibleChannelTypeIds())
141 .withSupportedBridgeTypeUIDs(type.getSupportedBridgeTypeUIDs()).withProperties(type.getProperties())
144 String representationProperty = type.getRepresentationProperty();
145 if (representationProperty != null) {
146 result = result.withRepresentationProperty(representationProperty);
148 URI configDescriptionURI = type.getConfigDescriptionURI();
149 if (configDescriptionURI != null) {
150 result = result.withConfigDescriptionURI(configDescriptionURI);
152 String category = type.getCategory();
153 if (category != null) {
154 result = result.withCategory(category);
156 String description = type.getDescription();
157 if (description != null) {
158 result = result.withDescription(description);
165 * Return List of {@link ChannelGroupDefinition} for `ThingType` with `typeUID`. If the `ThingType` does not exist
166 * in the thingTypeRegistry yet, retrieve list of `ChannelGroupDefinition` for base type systeminfo:computer.
168 * @param typeUID UID for ThingType
169 * @return list of channel group definitions, empty list if no channel group definitions
171 public List<ChannelGroupDefinition> getChannelGroupDefinitions(ThingTypeUID typeUID) {
172 ThingType type = thingTypeRegistry.getThingType(typeUID);
174 type = thingTypeRegistry.getThingType(THING_TYPE_COMPUTER);
177 return type.getChannelGroupDefinitions();
179 logger.debug("Cannot retrieve channel group definitions, no base thing type found");
180 return Collections.emptyList();
185 * Create a new channel group definition with index appended to id and label.
187 * @param channelGroupID id of channel group without index
188 * @param channelGroupTypeID id ChannelGroupType for new channel group definition
190 * @return channel group definition, null if provided channelGroupTypeID cannot be found in ChannelGroupTypeRegistry
192 public @Nullable ChannelGroupDefinition createChannelGroupDefinitionWithIndex(String channelGroupID,
193 String channelGroupTypeID, int i) {
194 ChannelGroupTypeUID channelGroupTypeUID = new ChannelGroupTypeUID(BINDING_ID, channelGroupTypeID);
195 ChannelGroupType channelGroupType = channelGroupTypeRegistry.getChannelGroupType(channelGroupTypeUID);
196 if (channelGroupType == null) {
197 logger.debug("Cannot create channel group definition, group type {} invalid", channelGroupTypeID);
200 String index = String.valueOf(i);
201 return new ChannelGroupDefinition(channelGroupID + index, channelGroupTypeUID,
202 channelGroupType.getLabel() + " " + index, channelGroupType.getDescription());
206 * Create a new channel with index appended to id and label of an existing channel.
208 * @param thing containing the existing channel
209 * @param channelID id of channel without index
211 * @return channel, null if provided channelID does not match a channel, or no type can be retrieved for the
214 public @Nullable Channel createChannelWithIndex(Thing thing, String channelID, int i) {
215 Channel baseChannel = thing.getChannel(channelID);
216 if (baseChannel == null) {
217 logger.debug("Cannot create channel, ID {} invalid", channelID);
220 ChannelTypeUID channelTypeUID = baseChannel.getChannelTypeUID();
221 ChannelType channelType = channelTypeRegistry.getChannelType(channelTypeUID);
222 if (channelType == null) {
223 logger.debug("Cannot create channel, type {} invalid",
224 channelTypeUID != null ? channelTypeUID.getId() : "null");
227 ThingUID thingUID = thing.getUID();
228 String index = String.valueOf(i);
229 ChannelUID channelUID = new ChannelUID(thingUID, channelID + index);
230 ChannelBuilder builder = ChannelBuilder.create(channelUID).withType(channelTypeUID)
231 .withConfiguration(baseChannel.getConfiguration());
232 builder.withLabel(channelType.getLabel() + " " + index);
233 String description = channelType.getDescription();
234 if (description != null) {
235 builder.withDescription(description);
237 return builder.build();
241 * Store the channel configurations for a thing, to be able to restore them later when the thing handler for the
242 * same thing gets recreated with a new thing type. This is necessary because the
243 * {@link BaseThingHandler##changeThingType()} method reverts channel configurations to their defaults.
247 public void storeChannelsConfig(Thing thing) {
248 Map<String, Configuration> channelsConfig = thing.getChannels().stream()
249 .collect(Collectors.toMap(c -> c.getUID().getId(), c -> c.getConfiguration()));
250 thingChannelsConfig.put(thing.getUID(), channelsConfig);
254 * Restore previous channel configurations of matching channels when the thing handler gets recreated with a new
255 * thing type. Return an empty map if no channel configurations where stored. Before returning previous channel
256 * configurations, clear the store, so they can only be retrieved ones, immediately after a thing type change. See
257 * also {@link #storeChannelsConfig(Thing)}.
260 * @return Map of ChannelId and Configuration for the channel
262 public Map<String, Configuration> restoreChannelsConfig(ThingUID UID) {
263 Map<String, Configuration> configs = thingChannelsConfig.remove(UID);
264 return configs != null ? configs : Collections.emptyMap();