Java tutorial
/** * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0 * * SPDX-License-Identifier: EPL-2.0 */ package org.eclipse.smarthome.binding.openweathermap.internal.handler; import static org.eclipse.smarthome.binding.openweathermap.internal.OpenWeatherMapBindingConstants.*; import java.time.Instant; import java.time.ZoneId; import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.measure.Unit; import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.binding.openweathermap.internal.config.OpenWeatherMapLocationConfiguration; import org.eclipse.smarthome.binding.openweathermap.internal.connection.OpenWeatherMapCommunicationException; import org.eclipse.smarthome.binding.openweathermap.internal.connection.OpenWeatherMapConfigurationException; import org.eclipse.smarthome.binding.openweathermap.internal.connection.OpenWeatherMapConnection; import org.eclipse.smarthome.core.library.types.DateTimeType; import org.eclipse.smarthome.core.library.types.DecimalType; import org.eclipse.smarthome.core.library.types.PointType; import org.eclipse.smarthome.core.library.types.QuantityType; import org.eclipse.smarthome.core.library.types.RawType; import org.eclipse.smarthome.core.library.types.StringType; import org.eclipse.smarthome.core.thing.Channel; import org.eclipse.smarthome.core.thing.ChannelGroupUID; import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.thing.Thing; import org.eclipse.smarthome.core.thing.ThingStatus; import org.eclipse.smarthome.core.thing.ThingStatusDetail; import org.eclipse.smarthome.core.thing.ThingStatusInfo; import org.eclipse.smarthome.core.thing.ThingTypeUID; import org.eclipse.smarthome.core.thing.binding.BaseThingHandler; import org.eclipse.smarthome.core.thing.binding.ThingHandlerCallback; import org.eclipse.smarthome.core.thing.binding.builder.ChannelBuilder; import org.eclipse.smarthome.core.thing.type.ChannelGroupTypeUID; import org.eclipse.smarthome.core.thing.type.ChannelKind; import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.core.types.RefreshType; import org.eclipse.smarthome.core.types.State; import org.eclipse.smarthome.core.types.UnDefType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * The {@link AbstractOpenWeatherMapHandler} is responsible for handling commands, which are sent to one of the * channels. * * @author Christoph Weitkamp - Initial contribution */ @NonNullByDefault public abstract class AbstractOpenWeatherMapHandler extends BaseThingHandler { private final Logger logger = LoggerFactory.getLogger(AbstractOpenWeatherMapHandler.class); public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.unmodifiableSet( Stream.of(THING_TYPE_WEATHER_AND_FORECAST, THING_TYPE_UVINDEX).collect(Collectors.toSet())); // keeps track of the parsed location protected @Nullable PointType location; public AbstractOpenWeatherMapHandler(Thing thing) { super(thing); } @Override public void initialize() { OpenWeatherMapLocationConfiguration config = getConfigAs(OpenWeatherMapLocationConfiguration.class); boolean configValid = true; if (StringUtils.trimToNull(config.getLocation()) == null) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "@text/offline.conf-error-missing-location"); configValid = false; } try { location = new PointType(config.getLocation()); } catch (IllegalArgumentException e) { location = null; updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "@text/offline.conf-error-parsing-location"); configValid = false; } if (configValid) { updateStatus(ThingStatus.UNKNOWN); } } @Override public void handleCommand(ChannelUID channelUID, Command command) { if (command instanceof RefreshType) { updateChannel(channelUID); } else { logger.debug("The OpenWeatherMap binding is a read-only binding and cannot handle command '{}'.", command); } } @Override public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) { if (ThingStatus.ONLINE.equals(bridgeStatusInfo.getStatus()) && ThingStatusDetail.BRIDGE_OFFLINE.equals(getThing().getStatusInfo().getStatusDetail())) { updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE); } else if (ThingStatus.OFFLINE.equals(bridgeStatusInfo.getStatus()) && !ThingStatus.OFFLINE.equals(getThing().getStatus())) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE); } } /** * Updates OpenWeatherMap data for this location. * * @param connection {@link OpenWeatherMapConnection} instance */ public void updateData(OpenWeatherMapConnection connection) { try { if (requestData(connection)) { updateChannels(); updateStatus(ThingStatus.ONLINE); } } catch (OpenWeatherMapCommunicationException e) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getLocalizedMessage()); } catch (OpenWeatherMapConfigurationException e) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getLocalizedMessage()); } } /** * Requests the data from OpenWeatherMap API. * * @param connection {@link OpenWeatherMapConnection} instance * @return true, if the request for the OpenWeatherMap data was successful * @throws OpenWeatherMapCommunicationException * @throws OpenWeatherMapConfigurationException */ protected abstract boolean requestData(OpenWeatherMapConnection connection) throws OpenWeatherMapCommunicationException, OpenWeatherMapConfigurationException; /** * Updates all channels of this handler from the latest OpenWeatherMap data retrieved. */ private void updateChannels() { for (Channel channel : getThing().getChannels()) { ChannelUID channelUID = channel.getUID(); if (ChannelKind.STATE.equals(channel.getKind()) && channelUID.isInGroup() && channelUID.getGroupId() != null && isLinked(channelUID)) { updateChannel(channelUID); } } } /** * Updates the channel with the given UID from the latest OpenWeatherMap data retrieved. * * @param channelUID UID of the channel */ protected abstract void updateChannel(ChannelUID channelUID); protected State getDateTimeTypeState(@Nullable Integer value) { return (value == null) ? UnDefType.UNDEF : new DateTimeType( ZonedDateTime.ofInstant(Instant.ofEpochSecond(value.longValue()), ZoneId.systemDefault())); } protected State getDecimalTypeState(@Nullable Double value) { return (value == null) ? UnDefType.UNDEF : new DecimalType(value); } protected State getPointTypeState(@Nullable Double latitude, @Nullable Double longitude) { return ((latitude == null) || (longitude == null)) ? UnDefType.UNDEF : new PointType(new DecimalType(latitude), new DecimalType(longitude)); } protected State getRawTypeState(@Nullable RawType image) { return (image == null) ? UnDefType.UNDEF : image; } protected State getStringTypeState(@Nullable String value) { return (value == null) ? UnDefType.UNDEF : new StringType(value); } protected State getQuantityTypeState(@Nullable Number value, Unit<?> unit) { return (value == null) ? UnDefType.UNDEF : new QuantityType<>(value, unit); } protected List<Channel> createChannelsForGroup(String channelGroupId, ChannelGroupTypeUID channelGroupTypeUID) { logger.debug("Building channel group '{}' for thing '{}'.", channelGroupId, getThing().getUID()); List<Channel> channels = new ArrayList<>(); ThingHandlerCallback callback = getCallback(); if (callback != null) { for (ChannelBuilder channelBuilder : callback.createChannelBuilders( new ChannelGroupUID(getThing().getUID(), channelGroupId), channelGroupTypeUID)) { Channel newChannel = channelBuilder.build(), existingChannel = getThing().getChannel(newChannel.getUID().getId()); if (existingChannel != null) { logger.trace("Thing '{}' already has an existing channel '{}'. Omit adding new channel '{}'.", getThing().getUID(), existingChannel.getUID(), newChannel.getUID()); continue; } channels.add(newChannel); } } return channels; } protected List<Channel> removeChannelsOfGroup(String channelGroupId) { logger.debug("Removing channel group '{}' from thing '{}'.", channelGroupId, getThing().getUID()); return getThing().getChannelsOfGroup(channelGroupId); } }