/** * Copyright (c) 2014-2017 by the respective copyright holders. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ package org.eclipse.smarthome.core.thing.internal; import java.math.BigDecimal; import java.net.URI; import java.util.List; import org.eclipse.smarthome.config.core.ConfigDescription; import org.eclipse.smarthome.config.core.ConfigDescriptionParameter; import org.eclipse.smarthome.config.core.ConfigDescriptionParameter.Type; import org.eclipse.smarthome.config.core.ConfigDescriptionRegistry; import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.core.thing.Channel; import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.thing.ThingUID; import org.eclipse.smarthome.core.thing.binding.ThingFactory; import org.eclipse.smarthome.core.thing.binding.builder.ChannelBuilder; import org.eclipse.smarthome.core.thing.type.ChannelDefinition; import org.eclipse.smarthome.core.thing.type.ChannelGroupDefinition; import org.eclipse.smarthome.core.thing.type.ChannelGroupType; import org.eclipse.smarthome.core.thing.type.ChannelType; import org.eclipse.smarthome.core.thing.type.ThingType; import org.eclipse.smarthome.core.thing.type.TypeResolver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.Lists; /** * Utility methods for creation of Things. * * It is supposed to contain methods that are commonly shared between {@link ThingManager} and {@link ThingFactory}. * * @author Simon Kaufmann - Initial contribution and API * @author Kai Kreuzer - Changed creation of channels to not require a thing type * */ public class ThingFactoryHelper { private static Logger logger = LoggerFactory.getLogger(ThingFactory.class); /** * Create {@link Channel} instances for the given Thing. * * @param thingType the type of the Thing (must not be null) * @param thingUID the Thing's UID (must not be null) * @param configDescriptionRegistry {@link ConfigDescriptionRegistry} that will be used to initialize the * {@link Channel}s with their corresponding default values, if given. * @return a list of {@link Channel}s */ public static List<Channel> createChannels(ThingType thingType, ThingUID thingUID, ConfigDescriptionRegistry configDescriptionRegistry) { List<Channel> channels = Lists.newArrayList(); List<ChannelDefinition> channelDefinitions = thingType.getChannelDefinitions(); for (ChannelDefinition channelDefinition : channelDefinitions) { Channel channel = createChannel(channelDefinition, thingUID, null, configDescriptionRegistry); if (channel != null) { channels.add(channel); } } List<ChannelGroupDefinition> channelGroupDefinitions = thingType.getChannelGroupDefinitions(); for (ChannelGroupDefinition channelGroupDefinition : channelGroupDefinitions) { ChannelGroupType channelGroupType = TypeResolver.resolve(channelGroupDefinition.getTypeUID()); if (channelGroupType != null) { List<ChannelDefinition> channelGroupChannelDefinitions = channelGroupType.getChannelDefinitions(); for (ChannelDefinition channelDefinition : channelGroupChannelDefinitions) { Channel channel = createChannel(channelDefinition, thingUID, channelGroupDefinition.getId(), configDescriptionRegistry); if (channel != null) { channels.add(channel); } } } else { logger.warn( "Could not create channels for channel group '{}' for thing type '{}', because channel group type '{}' could not be found.", channelGroupDefinition.getId(), thingUID, channelGroupDefinition.getTypeUID()); } } return channels; } private static Channel createChannel(ChannelDefinition channelDefinition, ThingUID thingUID, String groupId, ConfigDescriptionRegistry configDescriptionRegistry) { ChannelType type = TypeResolver.resolve(channelDefinition.getChannelTypeUID()); if (type == null) { logger.warn( "Could not create channel '{}' for thing type '{}', because channel type '{}' could not be found.", channelDefinition.getId(), thingUID, channelDefinition.getChannelTypeUID()); return null; } ChannelBuilder channelBuilder = ChannelBuilder .create(new ChannelUID(thingUID, groupId, channelDefinition.getId()), type.getItemType()) .withType(type.getUID()).withDefaultTags(type.getTags()).withKind(type.getKind()); // If we want to override the label, add it... if (channelDefinition.getLabel() != null) { channelBuilder = channelBuilder.withLabel(channelDefinition.getLabel()); } // If we want to override the description, add it... if (channelDefinition.getDescription() != null) { channelBuilder = channelBuilder.withDescription(channelDefinition.getDescription()); } // Initialize channel configuration with default-values URI channelConfigDescriptionURI = type.getConfigDescriptionURI(); if (configDescriptionRegistry != null && channelConfigDescriptionURI != null) { ConfigDescription cd = configDescriptionRegistry.getConfigDescription(channelConfigDescriptionURI); if (cd != null) { Configuration config = new Configuration(); for (ConfigDescriptionParameter param : cd.getParameters()) { String defaultValue = param.getDefault(); if (defaultValue != null) { Object value = getDefaultValueAsCorrectType(param.getType(), defaultValue); if (value != null) { config.put(param.getName(), value); } } } channelBuilder = channelBuilder.withConfiguration(config); } } channelBuilder = channelBuilder.withProperties(channelDefinition.getProperties()); Channel channel = channelBuilder.build(); return channel; } /** * Map the provided (default) value of the given {@link Type} to the corresponding Java type. * * In case the provided value is supposed to be a number and cannot be converted into the target type correctly, * this method will return <code>null</code> while logging a warning. * * @param parameterType the {@link Type} of the value * @param defaultValue the value that should be converted * @return the given value as the corresponding Java type or <code>null</code> if the value could not be converted */ public static Object getDefaultValueAsCorrectType(Type parameterType, String defaultValue) { try { switch (parameterType) { case TEXT: return defaultValue; case BOOLEAN: return Boolean.parseBoolean(defaultValue); case INTEGER: return new BigDecimal(defaultValue); case DECIMAL: return new BigDecimal(defaultValue); default: return null; } } catch (NumberFormatException ex) { LoggerFactory.getLogger(ThingFactory.class).warn("Could not parse default value '" + defaultValue + "' as type '" + parameterType + "': " + ex.getMessage(), ex); return null; } } /** * Apply the {@link ThingType}'s default values to the given {@link Configuration}. * * @param configuration the {@link Configuration} where the default values should be added (may be null, but method * won't have any effect then) * @param thingType the {@link ThingType} where to look for the default values (must not be null) * @param configDescriptionRegistry the {@link ConfigDescriptionRegistry} to use (may be null, but method won't have * any * effect then) */ public static void applyDefaultConfiguration(Configuration configuration, ThingType thingType, ConfigDescriptionRegistry configDescriptionRegistry) { if (configDescriptionRegistry != null && configuration != null) { // Set default values to thing-configuration if (thingType.getConfigDescriptionURI() != null) { ConfigDescription thingConfigDescription = configDescriptionRegistry .getConfigDescription(thingType.getConfigDescriptionURI()); if (thingConfigDescription != null) { for (ConfigDescriptionParameter parameter : thingConfigDescription.getParameters()) { String defaultValue = parameter.getDefault(); if (defaultValue != null && configuration.get(parameter.getName()) == null) { Object value = ThingFactoryHelper.getDefaultValueAsCorrectType(parameter.getType(), defaultValue); if (value != null) { configuration.put(parameter.getName(), value); } } } } } } } }