]> git.basschouten.com Git - openhab-addons.git/blob
e54806436c26ad62a301b9b312d87a1ae72737d9
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 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
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`.
57  *
58  * @author Mark Herwege - Initial contribution
59  *
60  */
61 @NonNullByDefault
62 @Component(service = { SysteminfoThingTypeProvider.class, ThingTypeProvider.class })
63 public class SysteminfoThingTypeProvider extends AbstractStorageBasedTypeProvider {
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<ThingUID, Map<String, Configuration>> thingChannelsConfig = new HashMap<>();
71
72     @Activate
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;
80     }
81
82     /**
83      * Create thing type with the provided typeUID and add it to the thing type registry.
84      *
85      * @param typeUID
86      * @return false if base type UID `systeminfo:computer` cannot be found in the thingTypeRegistry
87      */
88     public boolean createThingType(ThingTypeUID typeUID) {
89         logger.trace("Creating thing type {}", typeUID);
90         return updateThingType(typeUID, getChannelGroupDefinitions(typeUID));
91     }
92
93     /**
94      * Update `ThingType`with `typeUID`, replacing the channel group definitions with `groupDefs`.
95      *
96      * @param typeUID
97      * @param groupDefs
98      * @return false if `typeUID` or its base type UID `systeminfo:computer` cannot be found in the thingTypeRegistry
99      */
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.");
106                 return false;
107             }
108         }
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();
113
114             putThingType(type);
115             return true;
116         } else {
117             logger.debug("Error adding channel groups");
118             return false;
119         }
120     }
121
122     /**
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.
125      *
126      * @param newTypeUID
127      * @param baseTypeUID
128      * @return the ThingTypeBuilder, null if `baseTypeUID` cannot be found in the thingTypeRegistry
129      */
130     private @Nullable ThingTypeBuilder createThingTypeBuilder(ThingTypeUID newTypeUID, ThingTypeUID baseTypeUID) {
131         ThingType type = thingTypeRegistry.getThingType(baseTypeUID);
132
133         if (type == null) {
134             return null;
135         }
136
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())
142                 .isListed(false);
143
144         String representationProperty = type.getRepresentationProperty();
145         if (representationProperty != null) {
146             result = result.withRepresentationProperty(representationProperty);
147         }
148         URI configDescriptionURI = type.getConfigDescriptionURI();
149         if (configDescriptionURI != null) {
150             result = result.withConfigDescriptionURI(configDescriptionURI);
151         }
152         String category = type.getCategory();
153         if (category != null) {
154             result = result.withCategory(category);
155         }
156         String description = type.getDescription();
157         if (description != null) {
158             result = result.withDescription(description);
159         }
160
161         return result;
162     }
163
164     /**
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.
167      *
168      * @param typeUID UID for ThingType
169      * @return list of channel group definitions, empty list if no channel group definitions
170      */
171     public List<ChannelGroupDefinition> getChannelGroupDefinitions(ThingTypeUID typeUID) {
172         ThingType type = thingTypeRegistry.getThingType(typeUID);
173         if (type == null) {
174             type = thingTypeRegistry.getThingType(THING_TYPE_COMPUTER);
175         }
176         if (type != null) {
177             return type.getChannelGroupDefinitions();
178         } else {
179             logger.debug("Cannot retrieve channel group definitions, no base thing type found");
180             return Collections.emptyList();
181         }
182     }
183
184     /**
185      * Create a new channel group definition with index appended to id and label.
186      *
187      * @param channelGroupID id of channel group without index
188      * @param channelGroupTypeID id ChannelGroupType for new channel group definition
189      * @param i index
190      * @return channel group definition, null if provided channelGroupTypeID cannot be found in ChannelGroupTypeRegistry
191      */
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);
198             return null;
199         }
200         String index = String.valueOf(i);
201         return new ChannelGroupDefinition(channelGroupID + index, channelGroupTypeUID,
202                 channelGroupType.getLabel() + " " + index, channelGroupType.getDescription());
203     }
204
205     /**
206      * Create a new channel with index appended to id and label of an existing channel.
207      *
208      * @param thing containing the existing channel
209      * @param channelID id of channel without index
210      * @param i index
211      * @return channel, null if provided channelID does not match a channel, or no type can be retrieved for the
212      *         provided channel
213      */
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);
218             return null;
219         }
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");
225             return null;
226         }
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);
236         }
237         return builder.build();
238     }
239
240     /**
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 org.openhab.core.thing.binding.BaseThingHandler#changeThingType()} method reverts channel configurations
244      * to their defaults.
245      *
246      * @param thing
247      */
248     public void storeChannelsConfig(Thing thing) {
249         Map<String, Configuration> channelsConfig = thing.getChannels().stream()
250                 .collect(Collectors.toMap(c -> c.getUID().getId(), c -> c.getConfiguration()));
251         thingChannelsConfig.put(thing.getUID(), channelsConfig);
252     }
253
254     /**
255      * Restore previous channel configurations of matching channels when the thing handler gets recreated with a new
256      * thing type. Return an empty map if no channel configurations where stored. Before returning previous channel
257      * configurations, clear the store, so they can only be retrieved ones, immediately after a thing type change. See
258      * also {@link #storeChannelsConfig(Thing)}.
259      *
260      * @param UID
261      * @return Map of ChannelId and Configuration for the channel
262      */
263     public Map<String, Configuration> restoreChannelsConfig(ThingUID UID) {
264         Map<String, Configuration> configs = thingChannelsConfig.remove(UID);
265         return configs != null ? configs : Collections.emptyMap();
266     }
267 }