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 {@link SysteminfoHandler}. The
54 * thing needs to be updated to add the groups. The `SysteminfoThingTypeProvider` OSGi service gives access to the
55 * `ThingTypeRegistry` and serves the updated `ThingType`.
57 * @author Mark Herwege - Initial contribution
61 @Component(service = { SysteminfoThingTypeProvider.class, ThingTypeProvider.class })
62 public class SysteminfoThingTypeProvider extends AbstractStorageBasedTypeProvider {
63 private final Logger logger = LoggerFactory.getLogger(SysteminfoThingTypeProvider.class);
65 private final ThingTypeRegistry thingTypeRegistry;
66 private final ChannelGroupTypeRegistry channelGroupTypeRegistry;
67 private final ChannelTypeRegistry channelTypeRegistry;
69 private final Map<ThingUID, Map<String, Configuration>> thingChannelsConfig = new HashMap<>();
72 public SysteminfoThingTypeProvider(@Reference ThingTypeRegistry thingTypeRegistry,
73 @Reference ChannelGroupTypeRegistry channelGroupTypeRegistry,
74 @Reference ChannelTypeRegistry channelTypeRegistry, @Reference StorageService storageService) {
75 super(storageService);
76 this.thingTypeRegistry = thingTypeRegistry;
77 this.channelGroupTypeRegistry = channelGroupTypeRegistry;
78 this.channelTypeRegistry = channelTypeRegistry;
82 * Create thing type with the provided typeUID and add it to the thing type registry.
85 * @return false if base type UID `systeminfo:computer` cannot be found in the thingTypeRegistry
87 public boolean createThingType(ThingTypeUID typeUID) {
88 logger.trace("Creating thing type {}", typeUID);
89 return updateThingType(typeUID, getChannelGroupDefinitions(typeUID));
93 * Update `ThingType`with `typeUID`, replacing the channel group definitions with `groupDefs`.
97 * @return false if `typeUID` or its base type UID `systeminfo:computer` cannot be found in the thingTypeRegistry
99 public boolean updateThingType(ThingTypeUID typeUID, List<ChannelGroupDefinition> groupDefs) {
100 ThingType baseType = thingTypeRegistry.getThingType(typeUID);
101 if (baseType == null) {
102 baseType = thingTypeRegistry.getThingType(THING_TYPE_COMPUTER);
103 if (baseType == null) {
104 logger.warn("Could not find base thing type in registry.");
108 ThingTypeBuilder builder = createThingTypeBuilder(typeUID, baseType.getUID());
109 if (builder != null) {
110 logger.trace("Adding channel group definitions to thing type");
111 ThingType type = builder.withChannelGroupDefinitions(groupDefs).build();
116 logger.debug("Error adding channel groups");
122 * Return a {@link ThingTypeBuilder} that can create an exact copy of the `ThingType` with `baseTypeUID`.
123 * Further build steps can be performed on the returned object before recreating the `ThingType` from the builder.
127 * @return the ThingTypeBuilder, null if `baseTypeUID` cannot be found in the thingTypeRegistry
129 private @Nullable ThingTypeBuilder createThingTypeBuilder(ThingTypeUID newTypeUID, ThingTypeUID baseTypeUID) {
130 ThingType type = thingTypeRegistry.getThingType(baseTypeUID);
136 ThingTypeBuilder result = ThingTypeBuilder.instance(newTypeUID, type.getLabel())
137 .withChannelGroupDefinitions(type.getChannelGroupDefinitions())
138 .withChannelDefinitions(type.getChannelDefinitions())
139 .withExtensibleChannelTypeIds(type.getExtensibleChannelTypeIds())
140 .withSupportedBridgeTypeUIDs(type.getSupportedBridgeTypeUIDs()).withProperties(type.getProperties())
143 String representationProperty = type.getRepresentationProperty();
144 if (representationProperty != null) {
145 result = result.withRepresentationProperty(representationProperty);
147 URI configDescriptionURI = type.getConfigDescriptionURI();
148 if (configDescriptionURI != null) {
149 result = result.withConfigDescriptionURI(configDescriptionURI);
151 String category = type.getCategory();
152 if (category != null) {
153 result = result.withCategory(category);
155 String description = type.getDescription();
156 if (description != null) {
157 result = result.withDescription(description);
164 * Return List of {@link ChannelGroupDefinition} for `ThingType` with `typeUID`. If the `ThingType` does not exist
165 * in the thingTypeRegistry yet, retrieve list of `ChannelGroupDefinition` for base type systeminfo:computer.
167 * @param typeUID UID for ThingType
168 * @return list of channel group definitions, empty list if no channel group definitions
170 public List<ChannelGroupDefinition> getChannelGroupDefinitions(ThingTypeUID typeUID) {
171 ThingType type = thingTypeRegistry.getThingType(typeUID);
173 type = thingTypeRegistry.getThingType(THING_TYPE_COMPUTER);
176 return type.getChannelGroupDefinitions();
178 logger.debug("Cannot retrieve channel group definitions, no base thing type found");
179 return Collections.emptyList();
184 * Create a new channel group definition with index appended to id and label.
186 * @param channelGroupID id of channel group without index
187 * @param channelGroupTypeID id ChannelGroupType for new channel group definition
189 * @return channel group definition, null if provided channelGroupTypeID cannot be found in ChannelGroupTypeRegistry
191 public @Nullable ChannelGroupDefinition createChannelGroupDefinitionWithIndex(String channelGroupID,
192 String channelGroupTypeID, int i) {
193 ChannelGroupTypeUID channelGroupTypeUID = new ChannelGroupTypeUID(BINDING_ID, channelGroupTypeID);
194 ChannelGroupType channelGroupType = channelGroupTypeRegistry.getChannelGroupType(channelGroupTypeUID);
195 if (channelGroupType == null) {
196 logger.debug("Cannot create channel group definition, group type {} invalid", channelGroupTypeID);
199 String index = String.valueOf(i);
200 return new ChannelGroupDefinition(channelGroupID + index, channelGroupTypeUID,
201 channelGroupType.getLabel() + " " + index, channelGroupType.getDescription());
205 * Create a new channel with index appended to id and label of an existing channel.
207 * @param thing containing the existing channel
208 * @param channelID id of channel without index
210 * @return channel, null if provided channelID does not match a channel, or no type can be retrieved for the
213 public @Nullable Channel createChannelWithIndex(Thing thing, String channelID, int i) {
214 Channel baseChannel = thing.getChannel(channelID);
215 if (baseChannel == null) {
216 logger.debug("Cannot create channel, ID {} invalid", channelID);
219 ChannelTypeUID channelTypeUID = baseChannel.getChannelTypeUID();
220 ChannelType channelType = channelTypeRegistry.getChannelType(channelTypeUID);
221 if (channelType == null) {
222 logger.debug("Cannot create channel, type {} invalid",
223 channelTypeUID != null ? channelTypeUID.getId() : "null");
226 ThingUID thingUID = thing.getUID();
227 String index = String.valueOf(i);
228 ChannelUID channelUID = new ChannelUID(thingUID, channelID + index);
229 ChannelBuilder builder = ChannelBuilder.create(channelUID).withType(channelTypeUID)
230 .withConfiguration(baseChannel.getConfiguration());
231 builder.withLabel(channelType.getLabel() + " " + index);
232 String description = channelType.getDescription();
233 if (description != null) {
234 builder.withDescription(description);
236 return builder.build();
240 * Store the channel configurations for a thing, to be able to restore them later when the thing handler for the
241 * same thing gets recreated with a new thing type. This is necessary because the
242 * {@link BaseThingHandler##changeThingType()} method reverts channel configurations to their defaults.
246 public void storeChannelsConfig(Thing thing) {
247 Map<String, Configuration> channelsConfig = thing.getChannels().stream()
248 .collect(Collectors.toMap(c -> c.getUID().getId(), c -> c.getConfiguration()));
249 thingChannelsConfig.put(thing.getUID(), channelsConfig);
253 * Restore previous channel configurations of matching channels when the thing handler gets recreated with a new
254 * thing type. Return an empty map if no channel configurations where stored. Before returning previous channel
255 * configurations, clear the store, so they can only be retrieved ones, immediately after a thing type change. See
256 * also {@link #storeChannelsConfig(Thing)}.
259 * @return Map of ChannelId and Configuration for the channel
261 public Map<String, Configuration> restoreChannelsConfig(ThingUID UID) {
262 Map<String, Configuration> configs = thingChannelsConfig.remove(UID);
263 return configs != null ? configs : Collections.emptyMap();