Skip to content

Commit

Permalink
[Meteoalerte] Add an IconProvider (openhab#14811)
Browse files Browse the repository at this point in the history
* Solving activation / deactivation of IT4Wifi thing glitches.
* Some code enhancements
* Addition of an IconProvider

---------

Signed-off-by: clinique <[email protected]>
Signed-off-by: Thomas Burri <[email protected]>
  • Loading branch information
clinique authored and tburri-alstom committed Jun 19, 2023
1 parent 7b81628 commit e60af00
Show file tree
Hide file tree
Showing 31 changed files with 744 additions and 784 deletions.
57 changes: 38 additions & 19 deletions bundles/org.openhab.binding.meteoalerte/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Meteo Alerte Binding

The Meteo Alerte binding gives alert level regarding major weather related risk factors.
This binding provides its own icon set and provides appropriate static and dynamic SVG icons (see items examples below).

## Supported Things

Expand Down Expand Up @@ -58,6 +59,24 @@ The Météo Alerte information that are retrieved is available as these channels
| 2 | Orange | Be "very vigilant" in the concerned areas |
| 3 | Red | Absolute vigilance required |

## Provided icon set

This binding has its own IconProvider and makes available the following list of icons

| Icon Name | Dynamic | Illustration |
|---------------------------------|---------|--------------|
| oh:meteoalerte:vent | Yes | ![](src/main/resources/icon/vent.svg) |
| oh:meteoalerte:pluie-inondation | Yes | ![](src/main/resources/icon/pluie-inondation.svg) |
| oh:meteoalerte:orage | Yes | ![](src/main/resources/icon/orage.svg) |
| oh:meteoalerte:inondation | Yes | ![](src/main/resources/icon/inondation.svg) |
| oh:meteoalerte:neige | Yes | ![](src/main/resources/icon/neige.svg) |
| oh:meteoalerte:canicule | Yes | ![](src/main/resources/icon/canicule.svg) |
| oh:meteoalerte:grand-froid | Yes | ![](src/main/resources/icon/grand-froid.svg) |
| oh:meteoalerte:avalanches | Yes | ![](src/main/resources/icon/avalanches.svg) |
| oh:meteoalerte:vague-submersion | Yes | ![](src/main/resources/icon/vague-submersion.svg) |
| oh:meteoalerte:meteo_france | No | ![](src/main/resources/icon/meteo_france.svg) |


## Full Example

meteoalert.things:
Expand All @@ -69,26 +88,26 @@ Thing meteoalerte:department:yvelines @ "MyCity" [department="YVELINES", refresh
meteoalert.items:

```java
Group gMeteoAlert "Alertes Météo" <weather>
String MA_Dept78 "Département 78 [%s]" <aqi> (gMeteoAlert) {channel="meteoalerte:department:yvelines:comment"}
Number MA_etat_canicule "Canicule [%s]" <aqi> (gMeteoAlert) {channel="meteoalerte:department:yvelines:canicule"}
Number MA_etat_grand_froid "Grand Froid [%s]" <aqi> (gMeteoAlert) {channel="meteoalerte:department:yvelines:grand-froid"}
Number MA_etat_pluie_inondation "Pluie-Inondation [%s]" <aqi> (gMeteoAlert) {channel="meteoalerte:department:yvelines:pluie-inondation"}
Number MA_etat_neige "Neige [%s]" <aqi> (gMeteoAlert) {channel="meteoalerte:department:yvelines:neige"}
Number MA_etat_vent "Vent [%s]" <aqi> (gMeteoAlert) {channel="meteoalerte:department:yvelines:vent"}
Number MA_etat_inondation "Inondation [%s]" <aqi> (gMeteoAlert) {channel="meteoalerte:department:yvelines:inondation"}
Number MA_etat_orage "Orage [%s]" <aqi> (gMeteoAlert) {channel="meteoalerte:department:yvelines:orage"}
Number MA_etat_avalanche "Avalanches [%s]" <aqi> (gMeteoAlert) {channel="meteoalerte:department:yvelines:avalanches"}
Group gMeteoAlert "Alertes Météo" <oh:meteoalerte:meteo_france>
String MA_Dept78 "Département 78 [%s]" <text> (gMeteoAlert) {channel="meteoalerte:department:yvelines:comment"}
Number MA_etat_canicule "Canicule [%s]" <oh:meteoalerte:canicule> (gMeteoAlert) {channel="meteoalerte:department:yvelines:canicule"}
Number MA_etat_grand_froid "Grand Froid [%s]" <oh:meteoalerte:grand-froid> (gMeteoAlert) {channel="meteoalerte:department:yvelines:grand-froid"}
Number MA_etat_pluie_inondation "Pluie-Inondation [%s]" <oh:meteoalerte:pluie-inondation> (gMeteoAlert) {channel="meteoalerte:department:yvelines:pluie-inondation"}
Number MA_etat_neige "Neige [%s]" <oh:meteoalerte:neige> (gMeteoAlert) {channel="meteoalerte:department:yvelines:neige"}
Number MA_etat_vent "Vent [%s]" <oh:meteoalerte:vent> (gMeteoAlert) {channel="meteoalerte:department:yvelines:vent"}
Number MA_etat_inondation "Inondation [%s]" <oh:meteoalerte:inondation> (gMeteoAlert) {channel="meteoalerte:department:yvelines:inondation"}
Number MA_etat_orage "Orage [%s]" <oh:meteoalerte:orage> (gMeteoAlert) {channel="meteoalerte:department:yvelines:orage"}
Number MA_etat_avalanche "Avalanches [%s]" <oh:meteoalerte:avalanches> (gMeteoAlert) {channel="meteoalerte:department:yvelines:avalanches"}

Image MA_icon_canicule "Canicule" <aqi> (gMeteoAlert) {channel="meteoalerte:department:yvelines:canicule-icon"}
Image MA_icon_grand_froid "Grand Froid" <aqi> (gMeteoAlert) {channel="meteoalerte:department:yvelines:grand-froid-icon"}
Image MA_icon_pluie_inondation "Pluie-Inondation" <aqi> (gMeteoAlert) {channel="meteoalerte:department:yvelines:pluie-inondation-icon"}
Image MA_icon_neige "Neige" <aqi> (gMeteoAlert) {channel="meteoalerte:department:yvelines:neige-icon"}
Image MA_icon_vent "Vent" <aqi> (gMeteoAlert) {channel="meteoalerte:department:yvelines:vent-icon"}
Image MA_icon_inondation "Inondation" <aqi> (gMeteoAlert) {channel="meteoalerte:department:yvelines:inondation-icon"}
Image MA_icon_orage "Orage" <aqi> (gMeteoAlert) {channel="meteoalerte:department:yvelines:orage-icon"}
Image MA_icon_avalanche "Avalanche" <aqi> (gMeteoAlert) {channel="meteoalerte:department:yvelines:avalanches-icon"}
Image MA_icon_canicule "Canicule" <oh:meteoalerte:canicule> (gMeteoAlert) {channel="meteoalerte:department:yvelines:canicule-icon"}
Image MA_icon_grand_froid "Grand Froid" <oh:meteoalerte:grand-froid> (gMeteoAlert) {channel="meteoalerte:department:yvelines:grand-froid-icon"}
Image MA_icon_pluie_inondation "Pluie-Inondation" <oh:meteoalerte:pluie-inondation> (gMeteoAlert) {channel="meteoalerte:department:yvelines:pluie-inondation-icon"}
Image MA_icon_neige "Neige" <oh:meteoalerte:neige> (gMeteoAlert) {channel="meteoalerte:department:yvelines:neige-icon"}
Image MA_icon_vent "Vent" <oh:meteoalerte:vent> (gMeteoAlert) {channel="meteoalerte:department:yvelines:vent-icon"}
Image MA_icon_inondation "Inondation" <oh:meteoalerte:inondation> (gMeteoAlert) {channel="meteoalerte:department:yvelines:inondation-icon"}
Image MA_icon_orage "Orage" <oh:meteoalerte:orage> (gMeteoAlert) {channel="meteoalerte:department:yvelines:orage-icon"}
Image MA_icon_avalanche "Avalanche" <oh:meteoalerte:avalanches> (gMeteoAlert) {channel="meteoalerte:department:yvelines:avalanches-icon"}

DateTime MA_ObservationTS "Timestamp [%1$tH:%1$tM]" <time> (gMeteoAlert) {channel="meteoalerte:department:yvelines:observation-time"}
DateTime MA_ObservationTS "Timestamp [%1$tH:%1$tM]" <time> (gMeteoAlert) {channel="meteoalerte:department:yvelines:observation-time"}

```
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/**
* Copyright (c) 2010-2023 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* 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.openhab.binding.meteoalerte.internal;

import static org.openhab.binding.meteoalerte.internal.MeteoAlerteBindingConstants.*;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
import java.util.Set;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.meteoalerte.internal.json.ResponseFieldDTO.AlertLevel;
import org.openhab.core.i18n.TranslationProvider;
import org.openhab.core.ui.icon.IconProvider;
import org.openhab.core.ui.icon.IconSet;
import org.openhab.core.ui.icon.IconSet.Format;
import org.osgi.framework.BundleContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* The {@link MeteoAlertIconProvider} is the class providing binding related icons.
*
* @author Gaël L'hopital - Initial contribution
*/
@Component(service = { IconProvider.class, MeteoAlertIconProvider.class })
@NonNullByDefault
public class MeteoAlertIconProvider implements IconProvider {
private static final String DEFAULT_LABEL = "Météo Alerte Icons";
private static final String DEFAULT_DESCRIPTION = "Icons illustrating weather events provided by Météo Alerte";
private static final Set<String> ICONS = Set.of(WAVE, AVALANCHE, HEAT, FREEZE, FLOOD, SNOW, STORM, RAIN, WIND,
"meteo_france");

private final Logger logger = LoggerFactory.getLogger(MeteoAlertIconProvider.class);
private final BundleContext context;
private final TranslationProvider i18nProvider;

@Activate
public MeteoAlertIconProvider(final BundleContext context, final @Reference TranslationProvider i18nProvider) {
this.context = context;
this.i18nProvider = i18nProvider;
}

@Override
public Set<IconSet> getIconSets() {
return getIconSets(null);
}

@Override
public Set<IconSet> getIconSets(@Nullable Locale locale) {
String label = getText("label", DEFAULT_LABEL, locale);
String description = getText("decription", DEFAULT_DESCRIPTION, locale);

return Set.of(new IconSet(BINDING_ID, label, description, Set.of(Format.SVG)));
}

private String getText(String entry, String defaultValue, @Nullable Locale locale) {
String text = defaultValue;
if (locale != null) {
text = i18nProvider.getText(context.getBundle(), "iconset." + entry, defaultValue, locale);
text = text == null ? defaultValue : text;
}
return text;
}

@Override
public @Nullable Integer hasIcon(String category, String iconSetId, Format format) {
return ICONS.contains(category) && iconSetId.equals(BINDING_ID) && format == Format.SVG ? 0 : null;
}

public @Nullable InputStream getIcon(String category, String state) {
return getIcon(category, BINDING_ID, state, Format.SVG);
}

@Override
public @Nullable InputStream getIcon(String category, String iconSetId, @Nullable String state, Format format) {
String icon = getResource(category);
if (icon.isEmpty()) {
return null;
}

if (state != null) {
try {
Integer ordinal = Integer.valueOf(state);
AlertLevel alertLevel = ordinal < AlertLevel.values().length ? AlertLevel.values()[ordinal]
: AlertLevel.UNKNOWN;
icon = icon.replaceAll(AlertLevel.UNKNOWN.color, alertLevel.color);

} catch (NumberFormatException e) {
logger.debug("{} is not a valid DecimalType", state);
}
}

return new ByteArrayInputStream(icon.getBytes());
}

private String getResource(String iconName) {
String result = "";

URL iconResource = context.getBundle().getEntry("icon/%s.svg".formatted(iconName));
try (InputStream stream = iconResource.openStream()) {
result = new String(stream.readAllBytes(), StandardCharsets.UTF_8);
} catch (IOException e) {
logger.warn("Unable to load ressource '{}' : {}", iconResource.getPath(), e.getMessage());
}
return result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,12 @@
*/
@NonNullByDefault
public class MeteoAlerteBindingConstants {
public static final String BINDING_ID = "meteoalerte";

// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_METEO_ALERT = new ThingTypeUID("meteoalerte", "department");
public static final ThingTypeUID THING_TYPE_METEO_ALERT = new ThingTypeUID(BINDING_ID, "department");

// List of all Channel id's
// List of all Channel IDs
public static final String WAVE = "vague-submersion";
public static final String AVALANCHE = "avalanches";
public static final String HEAT = "canicule";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,20 @@
import com.google.gson.JsonDeserializer;

/**
* The {@link MeteoAlerteHandlerFactory} is responsible for creating things and thing
* handlers.
* The {@link MeteoAlerteHandlerFactory} is responsible for creating things and thing handlers.
*
* @author Gaël L'hopital - Initial contribution
*/
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.meteoalerte")
@Component(service = ThingHandlerFactory.class)
@NonNullByDefault
public class MeteoAlerteHandlerFactory extends BaseThingHandlerFactory {
private final Gson gson;
private final MeteoAlertIconProvider iconProvider;

@Activate
public MeteoAlerteHandlerFactory(@Reference TimeZoneProvider timeZoneProvider) {
public MeteoAlerteHandlerFactory(@Reference TimeZoneProvider timeZoneProvider,
@Reference MeteoAlertIconProvider iconProvider) {
this.iconProvider = iconProvider;
this.gson = new GsonBuilder().registerTypeAdapter(ZonedDateTime.class,
(JsonDeserializer<ZonedDateTime>) (json, type, jsonDeserializationContext) -> ZonedDateTime
.parse(json.getAsJsonPrimitive().getAsString())
Expand All @@ -62,6 +64,6 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) {
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();

return supportsThingType(thingTypeUID) ? new MeteoAlerteHandler(thing, gson) : null;
return supportsThingType(thingTypeUID) ? new MeteoAlerteHandler(thing, gson, iconProvider) : null;
}
}
Loading

0 comments on commit e60af00

Please sign in to comment.