]> git.basschouten.com Git - openhab-addons.git/blob
103eed41f6935e18a69061bc74535be613ec37c0
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.systeminfo.internal;
14
15 import static org.openhab.binding.systeminfo.internal.SysteminfoBindingConstants.*;
16
17 import java.net.URI;
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;
23 import java.util.Map;
24 import java.util.stream.Collectors;
25
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;
52
53 /**
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`.
57  *
58  * @author Mark Herwege - Initial contribution
59  *
60  */
61 @NonNullByDefault
62 @Component(service = { SysteminfoThingTypeProvider.class, ThingTypeProvider.class })
63 public class SysteminfoThingTypeProvider implements ThingTypeProvider {
64     private final Logger logger = LoggerFactory.getLogger(SysteminfoThingTypeProvider.class);
65
66     private final ThingTypeRegistry thingTypeRegistry;
67     private final ChannelGroupTypeRegistry channelGroupTypeRegistry;
68     private final ChannelTypeRegistry channelTypeRegistry;
69
70     private final Map<ThingTypeUID, ThingType> thingTypes = new HashMap<>();
71
72     private final Map<ThingUID, Map<String, Configuration>> thingChannelsConfig = new HashMap<>();
73
74     @Activate
75     public SysteminfoThingTypeProvider(@Reference ThingTypeRegistry thingTypeRegistry,
76             @Reference ChannelGroupTypeRegistry channelGroupTypeRegistry,
77             @Reference ChannelTypeRegistry channelTypeRegistry) {
78         super();
79         this.thingTypeRegistry = thingTypeRegistry;
80         this.channelGroupTypeRegistry = channelGroupTypeRegistry;
81         this.channelTypeRegistry = channelTypeRegistry;
82     }
83
84     @Override
85     public Collection<ThingType> getThingTypes(@Nullable Locale locale) {
86         return thingTypes.values();
87     }
88
89     @Override
90     public @Nullable ThingType getThingType(ThingTypeUID thingTypeUID, @Nullable Locale locale) {
91         return thingTypes.get(thingTypeUID);
92     }
93
94     private void setThingType(ThingTypeUID uid, ThingType type) {
95         thingTypes.put(uid, type);
96     }
97
98     /**
99      * Create thing type with the provided typeUID and add it to the thing type registry.
100      *
101      * @param typeUID
102      * @return false if base type UID `systeminfo:computer` cannot be found in the thingTypeRegistry
103      */
104     public boolean createThingType(ThingTypeUID typeUID) {
105         logger.trace("Creating thing type {}", typeUID);
106         return updateThingType(typeUID, getChannelGroupDefinitions(typeUID));
107     }
108
109     /**
110      * Update `ThingType`with `typeUID`, replacing the channel group definitions with `groupDefs`.
111      *
112      * @param typeUID
113      * @param groupDefs
114      * @return false if `typeUID` or its base type UID `systeminfo:computer` cannot be found in the thingTypeRegistry
115      */
116     public boolean updateThingType(ThingTypeUID typeUID, List<ChannelGroupDefinition> groupDefs) {
117         ThingTypeUID baseTypeUID = THING_TYPE_COMPUTER;
118         if (thingTypes.containsKey(typeUID)) {
119             baseTypeUID = typeUID;
120         }
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();
126
127             setThingType(typeUID, type);
128             return true;
129         } else {
130             logger.debug("Error adding channel groups");
131             return false;
132         }
133     }
134
135     /**
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.
138      *
139      * @param newTypeUID
140      * @param baseTypeUID
141      * @return the ThingTypeBuilder, null if `baseTypeUID` cannot be found in the thingTypeRegistry
142      */
143     private @Nullable ThingTypeBuilder createThingTypeBuilder(ThingTypeUID newTypeUID, ThingTypeUID baseTypeUID) {
144         ThingType type = thingTypeRegistry.getThingType(baseTypeUID);
145
146         if (type == null) {
147             return null;
148         }
149
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())
155                 .isListed(false);
156
157         String representationProperty = type.getRepresentationProperty();
158         if (representationProperty != null) {
159             result = result.withRepresentationProperty(representationProperty);
160         }
161         URI configDescriptionURI = type.getConfigDescriptionURI();
162         if (configDescriptionURI != null) {
163             result = result.withConfigDescriptionURI(configDescriptionURI);
164         }
165         String category = type.getCategory();
166         if (category != null) {
167             result = result.withCategory(category);
168         }
169         String description = type.getDescription();
170         if (description != null) {
171             result = result.withDescription(description);
172         }
173
174         return result;
175     }
176
177     /**
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.
180      *
181      * @param typeUID UID for ThingType
182      * @return list of channel group definitions, empty list if no channel group definitions
183      */
184     public List<ChannelGroupDefinition> getChannelGroupDefinitions(ThingTypeUID typeUID) {
185         ThingType type = thingTypeRegistry.getThingType(typeUID);
186         if (type == null) {
187             type = thingTypeRegistry.getThingType(THING_TYPE_COMPUTER);
188         }
189         if (type != null) {
190             return type.getChannelGroupDefinitions();
191         } else {
192             logger.debug("Cannot retrieve channel group definitions, no base thing type found");
193             return Collections.emptyList();
194         }
195     }
196
197     /**
198      * Create a new channel group definition with index appended to id and label.
199      *
200      * @param channelGroupID id of channel group without index
201      * @param channelGroupTypeID id ChannelGroupType for new channel group definition
202      * @param i index
203      * @return channel group definition, null if provided channelGroupTypeID cannot be found in ChannelGroupTypeRegistry
204      */
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);
211             return null;
212         }
213         String index = String.valueOf(i);
214         return new ChannelGroupDefinition(channelGroupID + index, channelGroupTypeUID,
215                 channelGroupType.getLabel() + " " + index, channelGroupType.getDescription());
216     }
217
218     /**
219      * Create a new channel with index appended to id and label of an existing channel.
220      *
221      * @param thing containing the existing channel
222      * @param channelID id of channel without index
223      * @param i index
224      * @return channel, null if provided channelID does not match a channel, or no type can be retrieved for the
225      *         provided channel
226      */
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);
231             return null;
232         }
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");
238             return null;
239         }
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);
249         }
250         return builder.build();
251     }
252
253     /**
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.
257      *
258      * @param thing
259      */
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);
264     }
265
266     /**
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)}.
271      *
272      * @param UID
273      * @return Map of ChannelId and Configuration for the channel
274      */
275     public Map<String, Configuration> restoreChannelsConfig(ThingUID UID) {
276         Map<String, Configuration> configs = thingChannelsConfig.remove(UID);
277         return configs != null ? configs : Collections.emptyMap();
278     }
279 }