]> git.basschouten.com Git - openhab-addons.git/blob
03cd6d20a7b498ff54e8e72d96db5c683a682aed
[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.Collections;
19 import java.util.HashMap;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.stream.Collectors;
23
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;
51
52 /**
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`.
56  *
57  * @author Mark Herwege - Initial contribution
58  *
59  */
60 @NonNullByDefault
61 @Component(service = { SysteminfoThingTypeProvider.class, ThingTypeProvider.class })
62 public class SysteminfoThingTypeProvider extends AbstractStorageBasedTypeProvider {
63     private final Logger logger = LoggerFactory.getLogger(SysteminfoThingTypeProvider.class);
64
65     private final ThingTypeRegistry thingTypeRegistry;
66     private final ChannelGroupTypeRegistry channelGroupTypeRegistry;
67     private final ChannelTypeRegistry channelTypeRegistry;
68
69     private final Map<ThingUID, Map<String, Configuration>> thingChannelsConfig = new HashMap<>();
70
71     @Activate
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;
79     }
80
81     /**
82      * Create thing type with the provided typeUID and add it to the thing type registry.
83      *
84      * @param typeUID
85      * @return false if base type UID `systeminfo:computer` cannot be found in the thingTypeRegistry
86      */
87     public boolean createThingType(ThingTypeUID typeUID) {
88         logger.trace("Creating thing type {}", typeUID);
89         return updateThingType(typeUID, getChannelGroupDefinitions(typeUID));
90     }
91
92     /**
93      * Update `ThingType`with `typeUID`, replacing the channel group definitions with `groupDefs`.
94      *
95      * @param typeUID
96      * @param groupDefs
97      * @return false if `typeUID` or its base type UID `systeminfo:computer` cannot be found in the thingTypeRegistry
98      */
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.");
105                 return false;
106             }
107         }
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();
112
113             putThingType(type);
114             return true;
115         } else {
116             logger.debug("Error adding channel groups");
117             return false;
118         }
119     }
120
121     /**
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.
124      *
125      * @param newTypeUID
126      * @param baseTypeUID
127      * @return the ThingTypeBuilder, null if `baseTypeUID` cannot be found in the thingTypeRegistry
128      */
129     private @Nullable ThingTypeBuilder createThingTypeBuilder(ThingTypeUID newTypeUID, ThingTypeUID baseTypeUID) {
130         ThingType type = thingTypeRegistry.getThingType(baseTypeUID);
131
132         if (type == null) {
133             return null;
134         }
135
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())
141                 .isListed(false);
142
143         String representationProperty = type.getRepresentationProperty();
144         if (representationProperty != null) {
145             result = result.withRepresentationProperty(representationProperty);
146         }
147         URI configDescriptionURI = type.getConfigDescriptionURI();
148         if (configDescriptionURI != null) {
149             result = result.withConfigDescriptionURI(configDescriptionURI);
150         }
151         String category = type.getCategory();
152         if (category != null) {
153             result = result.withCategory(category);
154         }
155         String description = type.getDescription();
156         if (description != null) {
157             result = result.withDescription(description);
158         }
159
160         return result;
161     }
162
163     /**
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.
166      *
167      * @param typeUID UID for ThingType
168      * @return list of channel group definitions, empty list if no channel group definitions
169      */
170     public List<ChannelGroupDefinition> getChannelGroupDefinitions(ThingTypeUID typeUID) {
171         ThingType type = thingTypeRegistry.getThingType(typeUID);
172         if (type == null) {
173             type = thingTypeRegistry.getThingType(THING_TYPE_COMPUTER);
174         }
175         if (type != null) {
176             return type.getChannelGroupDefinitions();
177         } else {
178             logger.debug("Cannot retrieve channel group definitions, no base thing type found");
179             return Collections.emptyList();
180         }
181     }
182
183     /**
184      * Create a new channel group definition with index appended to id and label.
185      *
186      * @param channelGroupID id of channel group without index
187      * @param channelGroupTypeID id ChannelGroupType for new channel group definition
188      * @param i index
189      * @return channel group definition, null if provided channelGroupTypeID cannot be found in ChannelGroupTypeRegistry
190      */
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);
197             return null;
198         }
199         String index = String.valueOf(i);
200         return new ChannelGroupDefinition(channelGroupID + index, channelGroupTypeUID,
201                 channelGroupType.getLabel() + " " + index, channelGroupType.getDescription());
202     }
203
204     /**
205      * Create a new channel with index appended to id and label of an existing channel.
206      *
207      * @param thing containing the existing channel
208      * @param channelID id of channel without index
209      * @param i index
210      * @return channel, null if provided channelID does not match a channel, or no type can be retrieved for the
211      *         provided channel
212      */
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);
217             return null;
218         }
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");
224             return null;
225         }
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);
235         }
236         return builder.build();
237     }
238
239     /**
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.
243      *
244      * @param thing
245      */
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);
250     }
251
252     /**
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)}.
257      *
258      * @param UID
259      * @return Map of ChannelId and Configuration for the channel
260      */
261     public Map<String, Configuration> restoreChannelsConfig(ThingUID UID) {
262         Map<String, Configuration> configs = thingChannelsConfig.remove(UID);
263         return configs != null ? configs : Collections.emptyMap();
264     }
265 }