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.Collection;
19 import java.util.Collections;
20 import java.util.HashMap;
21 import java.util.List;
22 import java.util.Locale;
24 import java.util.stream.Collectors;
26 import org.eclipse.jdt.annotation.NonNullByDefault;
27 import org.eclipse.jdt.annotation.Nullable;
28 import org.openhab.core.config.core.Configuration;
29 import org.openhab.core.thing.Channel;
30 import org.openhab.core.thing.ChannelUID;
31 import org.openhab.core.thing.Thing;
32 import org.openhab.core.thing.ThingTypeUID;
33 import org.openhab.core.thing.ThingUID;
34 import org.openhab.core.thing.binding.BaseThingHandler;
35 import org.openhab.core.thing.binding.ThingTypeProvider;
36 import org.openhab.core.thing.binding.builder.ChannelBuilder;
37 import org.openhab.core.thing.type.ChannelGroupDefinition;
38 import org.openhab.core.thing.type.ChannelGroupType;
39 import org.openhab.core.thing.type.ChannelGroupTypeRegistry;
40 import org.openhab.core.thing.type.ChannelGroupTypeUID;
41 import org.openhab.core.thing.type.ChannelType;
42 import org.openhab.core.thing.type.ChannelTypeRegistry;
43 import org.openhab.core.thing.type.ChannelTypeUID;
44 import org.openhab.core.thing.type.ThingType;
45 import org.openhab.core.thing.type.ThingTypeBuilder;
46 import org.openhab.core.thing.type.ThingTypeRegistry;
47 import org.osgi.service.component.annotations.Activate;
48 import org.osgi.service.component.annotations.Component;
49 import org.osgi.service.component.annotations.Reference;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
54 * Extended channels can be auto discovered and added to newly created groups in the {@link 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 implements ThingTypeProvider {
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<ThingTypeUID, ThingType> thingTypes = new HashMap<>();
72 private final Map<ThingUID, Map<String, Configuration>> thingChannelsConfig = new HashMap<>();
75 public SysteminfoThingTypeProvider(@Reference ThingTypeRegistry thingTypeRegistry,
76 @Reference ChannelGroupTypeRegistry channelGroupTypeRegistry,
77 @Reference ChannelTypeRegistry channelTypeRegistry) {
79 this.thingTypeRegistry = thingTypeRegistry;
80 this.channelGroupTypeRegistry = channelGroupTypeRegistry;
81 this.channelTypeRegistry = channelTypeRegistry;
85 public Collection<ThingType> getThingTypes(@Nullable Locale locale) {
86 return thingTypes.values();
90 public @Nullable ThingType getThingType(ThingTypeUID thingTypeUID, @Nullable Locale locale) {
91 return thingTypes.get(thingTypeUID);
94 private void setThingType(ThingTypeUID uid, ThingType type) {
95 thingTypes.put(uid, type);
99 * Create thing type with the provided typeUID and add it to the thing type registry.
102 * @return false if base type UID `systeminfo:computer` cannot be found in the thingTypeRegistry
104 public boolean createThingType(ThingTypeUID typeUID) {
105 logger.trace("Creating thing type {}", typeUID);
106 return updateThingType(typeUID, getChannelGroupDefinitions(typeUID));
110 * Update `ThingType`with `typeUID`, replacing the channel group definitions with `groupDefs`.
114 * @return false if `typeUID` or its base type UID `systeminfo:computer` cannot be found in the thingTypeRegistry
116 public boolean updateThingType(ThingTypeUID typeUID, List<ChannelGroupDefinition> groupDefs) {
117 ThingTypeUID baseTypeUID = THING_TYPE_COMPUTER;
118 if (thingTypes.containsKey(typeUID)) {
119 baseTypeUID = typeUID;
121 ThingType baseType = thingTypeRegistry.getThingType(baseTypeUID);
122 ThingTypeBuilder builder = createThingTypeBuilder(typeUID, baseTypeUID);
123 if (baseType != null && builder != null) {
124 logger.trace("Adding channel group definitions to thing type");
125 ThingType type = builder.withChannelGroupDefinitions(groupDefs).build();
127 setThingType(typeUID, type);
130 logger.debug("Error adding channel groups");
136 * Return a {@link ThingTypeBuilder} that can create an exact copy of the `ThingType` with `baseTypeUID`.
137 * Further build steps can be performed on the returned object before recreating the `ThingType` from the builder.
141 * @return the ThingTypeBuilder, null if `baseTypeUID` cannot be found in the thingTypeRegistry
143 private @Nullable ThingTypeBuilder createThingTypeBuilder(ThingTypeUID newTypeUID, ThingTypeUID baseTypeUID) {
144 ThingType type = thingTypeRegistry.getThingType(baseTypeUID);
150 ThingTypeBuilder result = ThingTypeBuilder.instance(newTypeUID, type.getLabel())
151 .withChannelGroupDefinitions(type.getChannelGroupDefinitions())
152 .withChannelDefinitions(type.getChannelDefinitions())
153 .withExtensibleChannelTypeIds(type.getExtensibleChannelTypeIds())
154 .withSupportedBridgeTypeUIDs(type.getSupportedBridgeTypeUIDs()).withProperties(type.getProperties())
157 String representationProperty = type.getRepresentationProperty();
158 if (representationProperty != null) {
159 result = result.withRepresentationProperty(representationProperty);
161 URI configDescriptionURI = type.getConfigDescriptionURI();
162 if (configDescriptionURI != null) {
163 result = result.withConfigDescriptionURI(configDescriptionURI);
165 String category = type.getCategory();
166 if (category != null) {
167 result = result.withCategory(category);
169 String description = type.getDescription();
170 if (description != null) {
171 result = result.withDescription(description);
178 * Return List of {@link ChannelGroupDefinition} for `ThingType` with `typeUID`. If the `ThingType` does not exist
179 * in the thingTypeRegistry yet, retrieve list of `ChannelGroupDefinition` for base type systeminfo:computer.
181 * @param typeUID UID for ThingType
182 * @return list of channel group definitions, empty list if no channel group definitions
184 public List<ChannelGroupDefinition> getChannelGroupDefinitions(ThingTypeUID typeUID) {
185 ThingType type = thingTypeRegistry.getThingType(typeUID);
187 type = thingTypeRegistry.getThingType(THING_TYPE_COMPUTER);
190 return type.getChannelGroupDefinitions();
192 logger.debug("Cannot retrieve channel group definitions, no base thing type found");
193 return Collections.emptyList();
198 * Create a new channel group definition with index appended to id and label.
200 * @param channelGroupID id of channel group without index
201 * @param channelGroupTypeID id ChannelGroupType for new channel group definition
203 * @return channel group definition, null if provided channelGroupTypeID cannot be found in ChannelGroupTypeRegistry
205 public @Nullable ChannelGroupDefinition createChannelGroupDefinitionWithIndex(String channelGroupID,
206 String channelGroupTypeID, int i) {
207 ChannelGroupTypeUID channelGroupTypeUID = new ChannelGroupTypeUID(BINDING_ID, channelGroupTypeID);
208 ChannelGroupType channelGroupType = channelGroupTypeRegistry.getChannelGroupType(channelGroupTypeUID);
209 if (channelGroupType == null) {
210 logger.debug("Cannot create channel group definition, group type {} invalid", channelGroupTypeID);
213 String index = String.valueOf(i);
214 return new ChannelGroupDefinition(channelGroupID + index, channelGroupTypeUID,
215 channelGroupType.getLabel() + " " + index, channelGroupType.getDescription());
219 * Create a new channel with index appended to id and label of an existing channel.
221 * @param thing containing the existing channel
222 * @param channelID id of channel without index
224 * @return channel, null if provided channelID does not match a channel, or no type can be retrieved for the
227 public @Nullable Channel createChannelWithIndex(Thing thing, String channelID, int i) {
228 Channel baseChannel = thing.getChannel(channelID);
229 if (baseChannel == null) {
230 logger.debug("Cannot create channel, ID {} invalid", channelID);
233 ChannelTypeUID channelTypeUID = baseChannel.getChannelTypeUID();
234 ChannelType channelType = channelTypeRegistry.getChannelType(channelTypeUID);
235 if (channelType == null) {
236 logger.debug("Cannot create channel, type {} invalid",
237 channelTypeUID != null ? channelTypeUID.getId() : "null");
240 ThingUID thingUID = thing.getUID();
241 String index = String.valueOf(i);
242 ChannelUID channelUID = new ChannelUID(thingUID, channelID + index);
243 ChannelBuilder builder = ChannelBuilder.create(channelUID).withType(channelTypeUID)
244 .withConfiguration(baseChannel.getConfiguration());
245 builder.withLabel(channelType.getLabel() + " " + index);
246 String description = channelType.getDescription();
247 if (description != null) {
248 builder.withDescription(description);
250 return builder.build();
254 * Store the channel configurations for a thing, to be able to restore them later when the thing handler for the
255 * same thing gets recreated with a new thing type. This is necessary because the
256 * {@link BaseThingHandler#changeThingType()} method reverts channel configurations to their defaults.
260 public void storeChannelsConfig(Thing thing) {
261 Map<String, Configuration> channelsConfig = thing.getChannels().stream()
262 .collect(Collectors.toMap(c -> c.getUID().getId(), c -> c.getConfiguration()));
263 thingChannelsConfig.put(thing.getUID(), channelsConfig);
267 * Restore previous channel configurations of matching channels when the thing handler gets recreated with a new
268 * thing type. Return an empty map if no channel configurations where stored. Before returning previous channel
269 * configurations, clear the store, so they can only be retrieved ones, immediately after a thing type change. See
270 * also {@link #storeChannelsConfig(Thing)}.
273 * @return Map of ChannelId and Configuration for the channel
275 public Map<String, Configuration> restoreChannelsConfig(ThingUID UID) {
276 Map<String, Configuration> configs = thingChannelsConfig.remove(UID);
277 return configs != null ? configs : Collections.emptyMap();