Skip to content
This repository has been archived by the owner on May 7, 2020. It is now read-only.

Commit

Permalink
- Removed double thing status update (#6533)
Browse files Browse the repository at this point in the history
- Removed UTF-8 constant
- Store / cache image files in a local folder
- Reduced discovery timeout
- Added log output in case the daily forecast is not available

Signed-off-by: Christoph Weitkamp <github@christophweitkamp.de>
  • Loading branch information
cweitkamp authored and kaikreuzer committed Dec 3, 2018
1 parent 984400a commit 385b756
Show file tree
Hide file tree
Showing 8 changed files with 324 additions and 63 deletions.
Expand Up @@ -64,21 +64,21 @@ Once the parameters `forecastHours` or `forecastDays` will be changed, the avail

### Current Weather

| Channel Group ID | Channel ID | Item Type | Description |
|------------------|-----------------|----------------------|---------------------------------------------------|
| current | time-stamp | DateTime | Time of data observation. |
| current | condition | String | Current weather condition. |
| current | condition-id | String | Id of the current weather condition. **Advanced** |
| current | icon | Image | Icon representing the current weather condition. |
| current | temperature | Number:Temperature | Current temperature. |
| current | pressure | Number:Pressure | Current barometric pressure. |
| current | humidity | Number:Dimensionless | Current atmospheric humidity. |
| current | wind-speed | Number:Speed | Current wind speed. |
| current | wind-direction | Number:Angle | Current wind direction. |
| current | gust-speed | Number:Speed | Current gust speed. **Advanced** |
| current | cloudiness | Number:Dimensionless | Current cloudiness. |
| current | rain | Number:Length | Rain volume for the last three hours. |
| current | snow | Number:Length | Snow volume for the last three hours. |
| Channel Group ID | Channel ID | Item Type | Description |
|------------------|----------------|----------------------|---------------------------------------------------|
| current | time-stamp | DateTime | Time of data observation. |
| current | condition | String | Current weather condition. |
| current | condition-id | String | Id of the current weather condition. **Advanced** |
| current | icon | Image | Icon representing the current weather condition. |
| current | temperature | Number:Temperature | Current temperature. |
| current | pressure | Number:Pressure | Current barometric pressure. |
| current | humidity | Number:Dimensionless | Current atmospheric humidity. |
| current | wind-speed | Number:Speed | Current wind speed. |
| current | wind-direction | Number:Angle | Current wind direction. |
| current | gust-speed | Number:Speed | Current gust speed. **Advanced** |
| current | cloudiness | Number:Dimensionless | Current cloudiness. |
| current | rain | Number:Length | Rain volume for the last three hours. |
| current | snow | Number:Length | Snow volume for the last three hours. |

### 3 Hour Forecast

Expand Down
Expand Up @@ -19,9 +19,11 @@

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.smarthome.binding.openweathermap.internal.handler.OpenWeatherMapAPIHandler;

/**
* The {@link OpenWeatherMapAPIConfiguration} is the class used to match the bridge configuration.
* The {@link OpenWeatherMapAPIConfiguration} is the class used to match the {@link OpenWeatherMapAPIHandler}s
* configuration.
*
* @author Christoph Weitkamp - Initial contribution
*/
Expand Down
Expand Up @@ -18,6 +18,7 @@

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutionException;
Expand All @@ -35,6 +36,7 @@
import org.eclipse.smarthome.binding.openweathermap.internal.model.OpenWeatherMapJsonDailyForecastData;
import org.eclipse.smarthome.binding.openweathermap.internal.model.OpenWeatherMapJsonHourlyForecastData;
import org.eclipse.smarthome.binding.openweathermap.internal.model.OpenWeatherMapJsonWeatherData;
import org.eclipse.smarthome.binding.openweathermap.internal.utils.ByteArrayFileCache;
import org.eclipse.smarthome.core.cache.ExpiringCacheMap;
import org.eclipse.smarthome.core.library.types.PointType;
import org.eclipse.smarthome.core.library.types.RawType;
Expand All @@ -57,7 +59,9 @@ public class OpenWeatherMapConnection {

private final Logger logger = LoggerFactory.getLogger(OpenWeatherMapConnection.class);

private static final String UTF_8_ENCODING = "UTF-8";
private static final String PROPERTY_MESSAGE = "message";

private static final String PNG_CONTENT_TYPE = "image/png";

private static final String PARAM_APPID = "appid";
private static final String PARAM_UNITS = "units";
Expand All @@ -73,13 +77,13 @@ public class OpenWeatherMapConnection {
// 16 day / daily forecast (see https://openweathermap.org/forecast16)
private static final String DAILY_FORECAST_URL = "https://api.openweathermap.org/data/2.5/forecast/daily";
// Weather icons (see https://openweathermap.org/weather-conditions)
private static final String ICON_URL = "https://openweathermap.org/img/w/";
private static final String ICON_URL = "https://openweathermap.org/img/w/%s.png";

private final OpenWeatherMapAPIHandler handler;
private final HttpClient httpClient;

private static final ExpiringCacheMap<String, @Nullable RawType> IMAGE_CACHE = new ExpiringCacheMap<>(
TimeUnit.DAYS.toMillis(7));
private static final ByteArrayFileCache IMAGE_CACHE = new ByteArrayFileCache(
"org.eclipse.smarthome.binding.openweathermap");
private final ExpiringCacheMap<String, String> cache;

private final JsonParser parser = new JsonParser();
Expand Down Expand Up @@ -158,17 +162,31 @@ public OpenWeatherMapConnection(OpenWeatherMapAPIHandler handler, HttpClient htt
OpenWeatherMapJsonDailyForecastData.class);
}

public static @Nullable RawType getWeatherIcon(String iconKey) {
if (StringUtils.isEmpty(iconKey)) {
throw new IllegalArgumentException("Cannot download weather icon as icon key is null.");
/**
* Downloads the icon for the given icon id (see https://openweathermap.org/weather-conditions).
*
* @param iconId the id of the icon
* @return the weather icon as {@link RawType}
*/
public static @Nullable RawType getWeatherIcon(String iconId) {
if (StringUtils.isEmpty(iconId)) {
throw new IllegalArgumentException("Cannot download weather icon as icon id is null.");
}

return downloadWeatherIconFromCache(ICON_URL + iconKey + ".png");
return downloadWeatherIconFromCache(String.format(ICON_URL, iconId));
}

private static @Nullable RawType downloadWeatherIconFromCache(String url) {
// TODO store / cache icon file in a local folder
return IMAGE_CACHE.putIfAbsentAndGet(url, () -> downloadWeatherIcon(url));
if (IMAGE_CACHE.containsKey(url)) {
return new RawType(IMAGE_CACHE.get(url), PNG_CONTENT_TYPE);
} else {
RawType image = downloadWeatherIcon(url);
if (image != null) {
IMAGE_CACHE.put(url, image.getBytes());
return image;
}
}
return null;
}

private static @Nullable RawType downloadWeatherIcon(String url) {
Expand Down Expand Up @@ -206,7 +224,7 @@ private String buildURL(String url, Map<String, String> requestParams) {

private String encodeParam(String value) {
try {
return URLEncoder.encode(value, UTF_8_ENCODING);
return URLEncoder.encode(value, StandardCharsets.UTF_8.name());
} catch (UnsupportedEncodingException e) {
logger.debug("UnsupportedEncodingException occurred during execution: {}", e.getLocalizedMessage(), e);
return StringUtils.EMPTY;
Expand Down Expand Up @@ -265,8 +283,8 @@ private String uglifyApikey(String url) {

private String getErrorMessage(String response) {
JsonObject jsonResponse = parser.parse(response).getAsJsonObject();
if (jsonResponse.has("message")) {
return jsonResponse.get("message").getAsString();
if (jsonResponse.has(PROPERTY_MESSAGE)) {
return jsonResponse.get(PROPERTY_MESSAGE).getAsString();
}
return response;
}
Expand Down
Expand Up @@ -44,7 +44,7 @@ public class OpenWeatherMapDiscoveryService extends AbstractDiscoveryService {

private final Logger logger = LoggerFactory.getLogger(OpenWeatherMapDiscoveryService.class);

private static final int DISCOVERY_TIMEOUT_SECONDS = 30;
private static final int DISCOVERY_TIMEOUT_SECONDS = 2;
private static final int DISCOVERY_INTERVAL_SECONDS = 60;
private @Nullable ScheduledFuture<?> discoveryJob;
private final LocationProvider locationProvider;
Expand All @@ -55,8 +55,8 @@ public class OpenWeatherMapDiscoveryService extends AbstractDiscoveryService {
/**
* Creates an OpenWeatherMapLocationDiscoveryService.
*/
public OpenWeatherMapDiscoveryService(OpenWeatherMapAPIHandler bridgeHandler,
LocationProvider locationProvider, LocaleProvider localeProvider, TranslationProvider i18nProvider) {
public OpenWeatherMapDiscoveryService(OpenWeatherMapAPIHandler bridgeHandler, LocationProvider locationProvider,
LocaleProvider localeProvider, TranslationProvider i18nProvider) {
super(AbstractOpenWeatherMapHandler.SUPPORTED_THING_TYPES, DISCOVERY_TIMEOUT_SECONDS);
this.bridgeHandler = bridgeHandler;
this.locationProvider = locationProvider;
Expand Down
Expand Up @@ -17,7 +17,9 @@
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 javax.measure.Unit;
Expand All @@ -36,13 +38,18 @@
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;
Expand Down Expand Up @@ -73,7 +80,6 @@ public AbstractOpenWeatherMapHandler(Thing thing) {

@Override
public void initialize() {
logger.debug("Initialize OpenWeatherMap handler {}.", getThing().getUID());
OpenWeatherMapLocationConfiguration config = getConfigAs(OpenWeatherMapLocationConfiguration.class);

boolean configValid = true;
Expand Down Expand Up @@ -152,7 +158,8 @@ protected abstract boolean requestData(OpenWeatherMapConnection connection)
private void updateChannels() {
for (Channel channel : getThing().getChannels()) {
ChannelUID channelUID = channel.getUID();
if (isLinked(channelUID.getId()) && channelUID.isInGroup() && channelUID.getGroupId() != null) {
if (ChannelKind.STATE.equals(channel.getKind()) && channelUID.isInGroup() && channelUID.getGroupId() != null
&& isLinked(channelUID)) {
updateChannel(channelUID);
}
}
Expand Down Expand Up @@ -187,4 +194,22 @@ protected State getStringTypeState(@Nullable String 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)) {
channels.add(channelBuilder.build());
}
}
return channels;
}

protected List<Channel> removeChannelsOfGroup(String channelGroupId) {
logger.debug("Removing channel group '{}' from thing '{}'.", channelGroupId, getThing().getUID());
return getThing().getChannelsOfGroup(channelGroupId);
}
}
Expand Up @@ -71,7 +71,7 @@ public OpenWeatherMapAPIHandler(Bridge bridge, HttpClient httpClient, LocaleProv

@Override
public void initialize() {
logger.debug("Initialize OpenWeatherMap API handler.");
logger.debug("Initialize OpenWeatherMap API handler '{}'.", getThing().getUID());
config = getConfigAs(OpenWeatherMapAPIConfiguration.class);

boolean configValid = true;
Expand Down Expand Up @@ -119,7 +119,7 @@ public void initialize() {

@Override
public void dispose() {
logger.debug("Dispose OpenWeatherMap API handler.");
logger.debug("Dispose OpenWeatherMap API handler '{}'.", getThing().getUID());
if (refreshJob != null && !refreshJob.isCancelled()) {
logger.debug("Stop refresh job.");
if (refreshJob.cancel(true)) {
Expand Down
Expand Up @@ -35,15 +35,11 @@
import org.eclipse.smarthome.config.core.Configuration;
import org.eclipse.smarthome.core.library.unit.SmartHomeUnits;
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.binding.ThingHandlerCallback;
import org.eclipse.smarthome.core.thing.binding.builder.ChannelBuilder;
import org.eclipse.smarthome.core.thing.binding.builder.ThingBuilder;
import org.eclipse.smarthome.core.thing.type.ChannelGroupTypeUID;
import org.eclipse.smarthome.core.types.State;
import org.eclipse.smarthome.core.types.UnDefType;
import org.slf4j.Logger;
Expand Down Expand Up @@ -84,7 +80,7 @@ public OpenWeatherMapWeatherAndForecastHandler(Thing thing) {
@Override
public void initialize() {
super.initialize();
logger.debug("Initialize OpenWeatherMapWeatherAndForecastHandler handler {}.", getThing().getUID());
logger.debug("Initialize OpenWeatherMapWeatherAndForecastHandler handler '{}'.", getThing().getUID());
OpenWeatherMapWeatherAndForecastConfiguration config = getConfigAs(
OpenWeatherMapWeatherAndForecastConfiguration.class);

Expand Down Expand Up @@ -157,8 +153,6 @@ public void initialize() {
builder.withChannel(channel);
}
updateThing(builder.build());

updateStatus(ThingStatus.UNKNOWN);
}
}

Expand All @@ -176,13 +170,15 @@ protected boolean requestData(OpenWeatherMapConnection connection)
dailyForecastData = connection.getDailyForecastData(location, forecastDays);
} catch (OpenWeatherMapConfigurationException e) {
if (e.getCause() instanceof HttpResponseException) {
logger.warn(e.getLocalizedMessage());
forecastDays = 0;
Configuration editConfig = editConfiguration();
editConfig.put(CONFIG_FORECAST_DAYS, 0);
updateConfiguration(editConfig);
logger.debug("Removing daily forecast channel groups.");
List<Channel> channels = getThing().getChannels().stream()
.filter(c -> c.getUID().getGroupId().startsWith(CHANNEL_GROUP_FORECAST_TODAY)
|| c.getUID().getGroupId().startsWith(CHANNEL_GROUP_FORECAST_TOMORROW)
.filter(c -> CHANNEL_GROUP_FORECAST_TODAY.equals(c.getUID().getGroupId())
|| CHANNEL_GROUP_FORECAST_TOMORROW.equals(c.getUID().getGroupId())
|| c.getUID().getGroupId().startsWith(CHANNEL_GROUP_DAILY_FORECAST_PREFIX))
.collect(Collectors.toList());
updateThing(editThing().withoutChannels(channels).build());
Expand Down Expand Up @@ -438,22 +434,4 @@ private void updateDailyForecastChannel(ChannelUID channelUID, int count) {
logger.debug("No weather data available to update channel '{}' of group '{}'.", channelId, channelGroupId);
}
}

private 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)) {
channels.add(channelBuilder.build());
}
}
return channels;
}

private List<Channel> removeChannelsOfGroup(String channelGroupId) {
logger.debug("Removing channel group '{}' from thing '{}'.", channelGroupId, getThing().getUID());
return getThing().getChannelsOfGroup(channelGroupId);
}
}

0 comments on commit 385b756

Please sign in to comment.