From: Gaƫl L'hopital Date: Fri, 15 Mar 2024 10:24:27 +0000 (+0100) Subject: [netatmo] Fix erroneous local URL handling (#16492) X-Git-Url: https://git.basschouten.com/?a=commitdiff_plain;h=03549dc5ba7e601d1ad10d0525e7ab5ce5273009;p=openhab-addons.git [netatmo] Fix erroneous local URL handling (#16492) * Wrong local URL handling * Adds configuration element ipAddress to Cameras (Welcome, Doorbell, Presence) so they remain reachable if API answer is in an incorrect network * Removing CameraConfiguration so binding does not break if thing configuration is not up-to-date --------- Signed-off-by: clinique Signed-off-by: gael@lhopital.org --- diff --git a/bundles/org.openhab.binding.netatmo/README.md b/bundles/org.openhab.binding.netatmo/README.md index 83b19cb45a..7312a1de13 100644 --- a/bundles/org.openhab.binding.netatmo/README.md +++ b/bundles/org.openhab.binding.netatmo/README.md @@ -77,18 +77,18 @@ Once authentication process has been done, current refreshToken is stored in `/O | Thing Type | Type | Netatmo Object | Description | Thing Parameters | | --------------- | ------ | -------------- | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | | account | Bridge | N/A | This bridge represents an account, gateway to Netatmo API. | clientId, clientSecret, username, password, webHookUrl, reconnectInterval | -| home | Bridge | NAHome | A home hosting Security or Energy devices and modules. | id, refreshInterval | +| home | Bridge | NAHome | A home hosting Security or Energy devices and modules. | id, refreshInterval, energyId, securityId | | person | Thing | NAPerson | A person known by your Netatmo system. | id | -| welcome | Thing | NACamera | The Netatmo Smart Indoor Camera (Welcome). | id | -| presence | Thing | NOC | The Netatmo Smart Outdoor Camera (Presence) camera with or without siren. | id | +| welcome | Thing | NACamera | The Netatmo Smart Indoor Camera (Welcome). | id, ipAddress | +| presence | Thing | NOC | The Netatmo Smart Outdoor Camera (Presence) camera with or without siren. | id, ipAddress | | siren | Thing | NIS | The Netatmo Smart Indoor Siren. | id | -| doorbell | Thing | NDB | The Netatmo Smart Video Doorbell device. | id | -| weather-station | Bridge | NAMain | Main indoor module reporting temperature, humidity, pressure, air quality and sound level. | id | +| doorbell | Thing | NDB | The Netatmo Smart Video Doorbell device. | id, ipAddress | +| weather-station | Bridge | NAMain | Main indoor module reporting temperature, humidity, pressure, air quality and sound level. | id, refreshInterval | | outdoor | Thing | NAModule1 | Outdoor module reporting temperature and humidity. | id | | wind | Thing | NAModule2 | Wind sensor reporting wind angle and strength. | id | | rain | Thing | NAModule3 | Rain Gauge measuring precipitation. | id | | indoor | Thing | NAModule4 | Additional indoor module reporting temperature, humidity and CO2 level. | id | -| home-coach | Thing | NHC | Healthy home coach reporting health-index, temperature, humidity, pressure, air quality, sound level. | id | +| home-coach | Thing | NHC | Healthy home coach reporting health-index, temperature, humidity, pressure, air quality, sound level. | id, refreshInterval | | plug | Thing | NAPlug | The relay connected to the boiler controlling a Thermostat and zero or more valves. | id | | thermostat | Thing | NATherm1 | The Thermostat device placed in a given room. | id | | room | Thing | NARoom | A room in your house. | id | diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/data/ModuleType.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/data/ModuleType.java index 575bc05219..2ac81a0e8a 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/data/ModuleType.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/data/ModuleType.java @@ -64,67 +64,70 @@ import org.openhab.core.thing.ThingTypeUID; */ @NonNullByDefault public enum ModuleType { - UNKNOWN(FeatureArea.NONE, "", 1, null, Set.of()), + UNKNOWN(FeatureArea.NONE, "", 1, "virtual", null, Set.of()), - ACCOUNT(FeatureArea.NONE, "", 1, null, Set.of(), new ChannelGroup(ApiBridgeChannelHelper.class, GROUP_MONITORING)), + ACCOUNT(FeatureArea.NONE, "", 1, "api_bridge", null, Set.of(), + new ChannelGroup(ApiBridgeChannelHelper.class, GROUP_MONITORING)), - HOME(FeatureArea.NONE, "NAHome", 1, ACCOUNT, + HOME(FeatureArea.NONE, "NAHome", 1, "home", ACCOUNT, Set.of(DeviceCapability.class, HomeCapability.class, ChannelHelperCapability.class), new ChannelGroup(SecurityChannelHelper.class, GROUP_SECURITY_EVENT, GROUP_SECURITY), new ChannelGroup(EnergyChannelHelper.class, GROUP_ENERGY)), - PERSON(FeatureArea.SECURITY, "NAPerson", 1, HOME, Set.of(PersonCapability.class, ChannelHelperCapability.class), + PERSON(FeatureArea.SECURITY, "NAPerson", 1, "virtual", HOME, + Set.of(PersonCapability.class, ChannelHelperCapability.class), new ChannelGroup(PersonChannelHelper.class, GROUP_PERSON), new ChannelGroup(EventPersonChannelHelper.class, GROUP_PERSON_LAST_EVENT)), - WELCOME(FeatureArea.SECURITY, "NACamera", 1, HOME, Set.of(CameraCapability.class, ChannelHelperCapability.class), - ChannelGroup.SIGNAL, ChannelGroup.EVENT, + WELCOME(FeatureArea.SECURITY, "NACamera", 1, "camera", HOME, + Set.of(CameraCapability.class, ChannelHelperCapability.class), ChannelGroup.SIGNAL, ChannelGroup.EVENT, new ChannelGroup(CameraChannelHelper.class, GROUP_SECURITY_EVENT, GROUP_CAM_STATUS, GROUP_CAM_LIVE)), - TAG(FeatureArea.SECURITY, "NACamDoorTag", 1, WELCOME, Set.of(ChannelHelperCapability.class), ChannelGroup.SIGNAL, - ChannelGroup.BATTERY, ChannelGroup.TIMESTAMP, new ChannelGroup(DoorTagChannelHelper.class, GROUP_TAG)), + TAG(FeatureArea.SECURITY, "NACamDoorTag", 1, "device", WELCOME, Set.of(ChannelHelperCapability.class), + ChannelGroup.SIGNAL, ChannelGroup.BATTERY, ChannelGroup.TIMESTAMP, + new ChannelGroup(DoorTagChannelHelper.class, GROUP_TAG)), - SIREN(FeatureArea.SECURITY, "NIS", 1, WELCOME, Set.of(ChannelHelperCapability.class), ChannelGroup.SIGNAL, + SIREN(FeatureArea.SECURITY, "NIS", 1, "device", WELCOME, Set.of(ChannelHelperCapability.class), ChannelGroup.SIGNAL, ChannelGroup.BATTERY, ChannelGroup.TIMESTAMP, new ChannelGroup(SirenChannelHelper.class, GROUP_SIREN)), - PRESENCE(FeatureArea.SECURITY, "NOC", 1, HOME, Set.of(PresenceCapability.class, ChannelHelperCapability.class), - ChannelGroup.SIGNAL, ChannelGroup.EVENT, + PRESENCE(FeatureArea.SECURITY, "NOC", 1, "camera", HOME, + Set.of(PresenceCapability.class, ChannelHelperCapability.class), ChannelGroup.SIGNAL, ChannelGroup.EVENT, new ChannelGroup(PresenceChannelHelper.class, GROUP_SECURITY_EVENT, GROUP_CAM_STATUS, GROUP_CAM_LIVE, GROUP_PRESENCE), new ChannelGroup(EventCameraChannelHelper.class, GROUP_SUB_EVENT)), - DOORBELL(FeatureArea.SECURITY, "NDB", 1, HOME, Set.of(DoorbellCapability.class, ChannelHelperCapability.class), - ChannelGroup.SIGNAL, + DOORBELL(FeatureArea.SECURITY, "NDB", 1, "camera", HOME, + Set.of(DoorbellCapability.class, ChannelHelperCapability.class), ChannelGroup.SIGNAL, new ChannelGroup(CameraChannelHelper.class, GROUP_SECURITY_EVENT, GROUP_DOORBELL_STATUS, GROUP_DOORBELL_LIVE), new ChannelGroup(EventCameraChannelHelper.class, GROUP_DOORBELL_LAST_EVENT, GROUP_DOORBELL_SUB_EVENT)), - WEATHER_STATION(FeatureArea.WEATHER, "NAMain", 1, ACCOUNT, + WEATHER_STATION(FeatureArea.WEATHER, "NAMain", 1, "configurable", ACCOUNT, Set.of(DeviceCapability.class, WeatherCapability.class, MeasureCapability.class, ChannelHelperCapability.class), ChannelGroup.SIGNAL, ChannelGroup.HUMIDITY, ChannelGroup.TSTAMP_EXT, ChannelGroup.MEASURE, ChannelGroup.AIR_QUALITY, ChannelGroup.LOCATION, ChannelGroup.NOISE, ChannelGroup.TEMP_INSIDE_EXT, new ChannelGroup(PressureChannelHelper.class, MeasureClass.PRESSURE, GROUP_TYPE_PRESSURE_EXTENDED)), - OUTDOOR(FeatureArea.WEATHER, "NAModule1", 1, WEATHER_STATION, + OUTDOOR(FeatureArea.WEATHER, "NAModule1", 1, "device", WEATHER_STATION, Set.of(MeasureCapability.class, ChannelHelperCapability.class), ChannelGroup.SIGNAL, ChannelGroup.HUMIDITY, ChannelGroup.TSTAMP_EXT, ChannelGroup.MEASURE, ChannelGroup.BATTERY, ChannelGroup.TEMP_OUTSIDE_EXT), - WIND(FeatureArea.WEATHER, "NAModule2", 1, WEATHER_STATION, Set.of(ChannelHelperCapability.class), + WIND(FeatureArea.WEATHER, "NAModule2", 1, "device", WEATHER_STATION, Set.of(ChannelHelperCapability.class), ChannelGroup.SIGNAL, ChannelGroup.TSTAMP_EXT, ChannelGroup.BATTERY, new ChannelGroup(WindChannelHelper.class, GROUP_WIND)), - RAIN(FeatureArea.WEATHER, "NAModule3", 1, WEATHER_STATION, + RAIN(FeatureArea.WEATHER, "NAModule3", 1, "device", WEATHER_STATION, Set.of(MeasureCapability.class, ChannelHelperCapability.class), ChannelGroup.SIGNAL, ChannelGroup.TSTAMP_EXT, ChannelGroup.MEASURE, ChannelGroup.BATTERY, new ChannelGroup(RainChannelHelper.class, MeasureClass.RAIN_QUANTITY, GROUP_RAIN)), - INDOOR(FeatureArea.WEATHER, "NAModule4", 1, WEATHER_STATION, + INDOOR(FeatureArea.WEATHER, "NAModule4", 1, "device", WEATHER_STATION, Set.of(MeasureCapability.class, ChannelHelperCapability.class), ChannelGroup.SIGNAL, ChannelGroup.TSTAMP_EXT, ChannelGroup.MEASURE, ChannelGroup.BATTERY, ChannelGroup.HUMIDITY, ChannelGroup.TEMP_INSIDE_EXT, ChannelGroup.AIR_QUALITY), - HOME_COACH(FeatureArea.AIR_CARE, "NHC", 1, ACCOUNT, + HOME_COACH(FeatureArea.AIR_CARE, "NHC", 1, "configurable", ACCOUNT, Set.of(DeviceCapability.class, AirCareCapability.class, MeasureCapability.class, ChannelHelperCapability.class), ChannelGroup.LOCATION, ChannelGroup.SIGNAL, ChannelGroup.NOISE, ChannelGroup.HUMIDITY, @@ -132,24 +135,26 @@ public enum ModuleType { new ChannelGroup(AirQualityChannelHelper.class, GROUP_TYPE_AIR_QUALITY_EXTENDED), new ChannelGroup(PressureChannelHelper.class, MeasureClass.PRESSURE, GROUP_PRESSURE)), - PLUG(FeatureArea.ENERGY, "NAPlug", 1, HOME, Set.of(ChannelHelperCapability.class), ChannelGroup.SIGNAL), + PLUG(FeatureArea.ENERGY, "NAPlug", 1, "device", HOME, Set.of(ChannelHelperCapability.class), ChannelGroup.SIGNAL), - VALVE(FeatureArea.ENERGY, "NRV", 1, PLUG, Set.of(ChannelHelperCapability.class), ChannelGroup.SIGNAL, + VALVE(FeatureArea.ENERGY, "NRV", 1, "device", PLUG, Set.of(ChannelHelperCapability.class), ChannelGroup.SIGNAL, ChannelGroup.BATTERY_EXT), - THERMOSTAT(FeatureArea.ENERGY, "NATherm1", 1, PLUG, Set.of(ChannelHelperCapability.class), ChannelGroup.SIGNAL, - ChannelGroup.BATTERY_EXT, new ChannelGroup(Therm1ChannelHelper.class, GROUP_TYPE_TH_PROPERTIES)), + THERMOSTAT(FeatureArea.ENERGY, "NATherm1", 1, "device", PLUG, Set.of(ChannelHelperCapability.class), + ChannelGroup.SIGNAL, ChannelGroup.BATTERY_EXT, + new ChannelGroup(Therm1ChannelHelper.class, GROUP_TYPE_TH_PROPERTIES)), - ROOM(FeatureArea.ENERGY, "NARoom", 1, HOME, Set.of(RoomCapability.class, ChannelHelperCapability.class), + ROOM(FeatureArea.ENERGY, "NARoom", 1, "virtual", HOME, Set.of(RoomCapability.class, ChannelHelperCapability.class), new ChannelGroup(RoomChannelHelper.class, GROUP_TYPE_ROOM_PROPERTIES, GROUP_TYPE_ROOM_TEMPERATURE), new ChannelGroup(SetpointChannelHelper.class, GROUP_SETPOINT)), - SMOKE_DETECTOR(FeatureArea.SECURITY, "NSD", 1, HOME, + SMOKE_DETECTOR(FeatureArea.SECURITY, "NSD", 1, "device", HOME, Set.of(AlarmEventCapability.class, ChannelHelperCapability.class), ChannelGroup.SIGNAL, ChannelGroup.TIMESTAMP, ChannelGroup.ALARM_LAST_EVENT), - CO_DETECTOR(FeatureArea.SECURITY, "NCO", 1, HOME, Set.of(AlarmEventCapability.class, ChannelHelperCapability.class), - ChannelGroup.SIGNAL, ChannelGroup.TIMESTAMP, ChannelGroup.ALARM_LAST_EVENT); + CO_DETECTOR(FeatureArea.SECURITY, "NCO", 1, "device", HOME, + Set.of(AlarmEventCapability.class, ChannelHelperCapability.class), ChannelGroup.SIGNAL, + ChannelGroup.TIMESTAMP, ChannelGroup.ALARM_LAST_EVENT); public static final EnumSet AS_SET = EnumSet.allOf(ModuleType.class); @@ -160,8 +165,9 @@ public enum ModuleType { public final FeatureArea feature; public final String apiName; public final String thingTypeVersion; + public final URI configDescription; - ModuleType(FeatureArea feature, String apiName, int thingTypeVersion, @Nullable ModuleType bridge, + ModuleType(FeatureArea feature, String apiName, int thingTypeVersion, String config, @Nullable ModuleType bridge, Set> capabilities, ChannelGroup... channelGroups) { this.bridgeType = Optional.ofNullable(bridge); this.feature = feature; @@ -170,6 +176,7 @@ public enum ModuleType { this.channelGroups = Set.of(channelGroups); this.thingTypeUID = new ThingTypeUID(BINDING_ID, name().toLowerCase().replace("_", "-")); this.thingTypeVersion = Integer.toString(thingTypeVersion); + this.configDescription = URI.create(BINDING_ID + ":" + config); } public boolean isLogical() { @@ -203,13 +210,6 @@ public enum ModuleType { return bridgeType.orElse(UNKNOWN); } - public URI getConfigDescription() { - return URI.create(BINDING_ID + ":" - + (equals(ACCOUNT) ? "api_bridge" - : equals(HOME) ? "home" - : (isLogical() ? "virtual" : UNKNOWN.equals(getBridge()) ? "configurable" : "device"))); - } - public int getDepth() { ModuleType parent = getBridge(); return parent == UNKNOWN ? 1 : parent.getDepth() + 1; diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/CommonInterface.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/CommonInterface.java index bf1c8d5eb3..59e43d3016 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/CommonInterface.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/CommonInterface.java @@ -32,7 +32,6 @@ import org.openhab.binding.netatmo.internal.handler.capability.HomeCapability; import org.openhab.binding.netatmo.internal.handler.capability.ParentUpdateCapability; import org.openhab.binding.netatmo.internal.handler.capability.RefreshCapability; import org.openhab.binding.netatmo.internal.handler.capability.RestCapability; -import org.openhab.core.config.core.Configuration; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.Channel; import org.openhab.core.thing.ChannelUID; @@ -69,6 +68,10 @@ public interface CommonInterface { void updateState(ChannelUID channelUID, State state); + default void updateState(String groupId, String id, State state) { + updateState(new ChannelUID(getThing().getUID(), groupId, id), state); + } + void setThingStatus(ThingStatus thingStatus, ThingStatusDetail thingStatusDetail, @Nullable String thingStatusReason); @@ -108,11 +111,11 @@ public interface CommonInterface { } default String getId() { - return getConfiguration().as(NAThingConfiguration.class).getId(); + return getThingConfigAs(NAThingConfiguration.class).getId(); } - default Configuration getConfiguration() { - return getThing().getConfiguration(); + default T getThingConfigAs(Class configurationClass) { + return getThing().getConfiguration().as(configurationClass); } default Stream getActiveChannels() { diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/AlarmEventCapability.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/AlarmEventCapability.java index 4c559bceba..8e7c3a922b 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/AlarmEventCapability.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/AlarmEventCapability.java @@ -23,8 +23,7 @@ import org.openhab.binding.netatmo.internal.api.dto.WebhookEvent; import org.openhab.binding.netatmo.internal.handler.CommonInterface; import org.openhab.binding.netatmo.internal.handler.channelhelper.ChannelHelper; import org.openhab.binding.netatmo.internal.providers.NetatmoDescriptionProvider; -import org.openhab.core.thing.ChannelUID; -import org.openhab.core.thing.ThingUID; +import org.openhab.binding.netatmo.internal.utils.ChannelTypeUtils; import org.openhab.core.types.UnDefType; /** @@ -45,15 +44,12 @@ public class AlarmEventCapability extends HomeSecurityThingCapability { protected void updateWebhookEvent(WebhookEvent event) { super.updateWebhookEvent(event); - final ThingUID thingUid = handler.getThing().getUID(); - handler.updateState(new ChannelUID(thingUid, GROUP_LAST_EVENT, CHANNEL_EVENT_TYPE), - toStringType(event.getEventType())); - handler.updateState(new ChannelUID(thingUid, GROUP_LAST_EVENT, CHANNEL_EVENT_TIME), - toDateTimeType(event.getTime())); - handler.updateState(new ChannelUID(thingUid, GROUP_LAST_EVENT, CHANNEL_EVENT_SUBTYPE), - event.getSubTypeDescription().map(d -> toStringType(d)).orElse(UnDefType.NULL)); + handler.updateState(GROUP_LAST_EVENT, CHANNEL_EVENT_TYPE, toStringType(event.getEventType())); + handler.updateState(GROUP_LAST_EVENT, CHANNEL_EVENT_TIME, toDateTimeType(event.getTime())); + handler.updateState(GROUP_LAST_EVENT, CHANNEL_EVENT_SUBTYPE, + event.getSubTypeDescription().map(ChannelTypeUtils::toStringType).orElse(UnDefType.NULL)); final String message = event.getName(); - handler.updateState(new ChannelUID(thingUid, GROUP_LAST_EVENT, CHANNEL_EVENT_MESSAGE), + handler.updateState(GROUP_LAST_EVENT, CHANNEL_EVENT_MESSAGE, message == null || message.isBlank() ? UnDefType.NULL : toStringType(message)); } diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/CameraCapability.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/CameraCapability.java index 72b9fb5a8f..434ec81b31 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/CameraCapability.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/CameraCapability.java @@ -15,9 +15,13 @@ package org.openhab.binding.netatmo.internal.handler.capability; import static org.openhab.binding.netatmo.internal.NetatmoBindingConstants.*; import static org.openhab.binding.netatmo.internal.utils.ChannelTypeUtils.*; +import java.net.URI; import java.util.ArrayList; import java.util.List; +import javax.ws.rs.core.UriBuilder; +import javax.ws.rs.core.UriBuilderException; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.netatmo.internal.api.data.NetatmoConstants.AlimentationStatus; @@ -32,14 +36,15 @@ import org.openhab.binding.netatmo.internal.handler.CommonInterface; import org.openhab.binding.netatmo.internal.handler.channelhelper.CameraChannelHelper; import org.openhab.binding.netatmo.internal.handler.channelhelper.ChannelHelper; import org.openhab.binding.netatmo.internal.providers.NetatmoDescriptionProvider; +import org.openhab.core.config.core.Configuration; import org.openhab.core.library.types.OnOffType; import org.openhab.core.thing.ChannelUID; -import org.openhab.core.thing.Thing; -import org.openhab.core.thing.ThingUID; import org.openhab.core.types.Command; import org.openhab.core.types.State; import org.openhab.core.types.StateOption; import org.openhab.core.types.UnDefType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * {@link CameraCapability} give to handle Welcome Camera specifics @@ -49,6 +54,10 @@ import org.openhab.core.types.UnDefType; */ @NonNullByDefault public class CameraCapability extends HomeSecurityThingCapability { + private static final String IP_ADDRESS = "ipAddress"; + + private final Logger logger = LoggerFactory.getLogger(CameraCapability.class); + private final CameraChannelHelper cameraHelper; private final ChannelUID personChannelUID; @@ -60,7 +69,7 @@ public class CameraCapability extends HomeSecurityThingCapability { public CameraCapability(CommonInterface handler, NetatmoDescriptionProvider descriptionProvider, List channelHelpers) { super(handler, descriptionProvider, channelHelpers); - this.personChannelUID = new ChannelUID(thing.getUID(), GROUP_LAST_EVENT, CHANNEL_EVENT_PERSON_ID); + this.personChannelUID = new ChannelUID(thingUID, GROUP_LAST_EVENT, CHANNEL_EVENT_PERSON_ID); this.cameraHelper = (CameraChannelHelper) channelHelpers.stream().filter(c -> c instanceof CameraChannelHelper) .findFirst().orElseThrow(() -> new IllegalArgumentException( "CameraCapability must find a CameraChannelHelper, please file a bug report.")); @@ -68,7 +77,6 @@ public class CameraCapability extends HomeSecurityThingCapability { @Override public void initialize() { - Thing thing = handler.getThing(); hasSubEventGroup = !thing.getChannelsOfGroup(GROUP_SUB_EVENT).isEmpty(); hasLastEventGroup = !thing.getChannelsOfGroup(GROUP_LAST_EVENT).isEmpty(); } @@ -80,14 +88,15 @@ public class CameraCapability extends HomeSecurityThingCapability { String newVpnUrl = newData.getVpnUrl(); if (newVpnUrl != null && !newVpnUrl.equals(vpnUrl)) { // This will also decrease the number of requests emitted toward Netatmo API. - localUrl = newData.isLocal() ? getSecurityCapability().map(cap -> cap.ping(newVpnUrl)).orElse(null) : null; + localUrl = newData.isLocal() ? ping(newVpnUrl) : null; + logger.debug("localUrl set to {} for camera {}", localUrl, thingUID); cameraHelper.setUrls(newVpnUrl, localUrl); eventHelper.setUrls(newVpnUrl, localUrl); } vpnUrl = newVpnUrl; if (!SdCardStatus.SD_CARD_WORKING.equals(newData.getSdStatus()) || !AlimentationStatus.ALIM_CORRECT_POWER.equals(newData.getAlimStatus())) { - statusReason = String.format("%s, %s", newData.getSdStatus(), newData.getAlimStatus()); + statusReason = "%s, %s".formatted(newData.getSdStatus(), newData.getAlimStatus()); } } @@ -96,11 +105,11 @@ public class CameraCapability extends HomeSecurityThingCapability { super.updateWebhookEvent(event); if (hasSubEventGroup) { - updateSubGroup(event, thing.getUID(), GROUP_SUB_EVENT); + updateSubGroup(event, GROUP_SUB_EVENT); } if (hasLastEventGroup) { - updateSubGroup(event, thing.getUID(), GROUP_LAST_EVENT); + updateSubGroup(event, GROUP_LAST_EVENT); } // The channel should get triggered at last (after super and sub methods), because this allows rules to access @@ -111,19 +120,17 @@ public class CameraCapability extends HomeSecurityThingCapability { handler.triggerChannel(CHANNEL_HOME_EVENT, eventType); } - private void updateSubGroup(WebhookEvent event, ThingUID thingUid, String group) { - handler.updateState(new ChannelUID(thingUid, group, CHANNEL_EVENT_TYPE), toStringType(event.getEventType())); - handler.updateState(new ChannelUID(thingUid, group, CHANNEL_EVENT_TIME), toDateTimeType(event.getTime())); - handler.updateState(new ChannelUID(thingUid, group, CHANNEL_EVENT_SNAPSHOT), toRawType(event.getSnapshotUrl())); - handler.updateState(new ChannelUID(thingUid, group, CHANNEL_EVENT_SNAPSHOT_URL), - toStringType(event.getSnapshotUrl())); - handler.updateState(new ChannelUID(thingUid, group, CHANNEL_EVENT_VIGNETTE), toRawType(event.getVignetteUrl())); - handler.updateState(new ChannelUID(thingUid, group, CHANNEL_EVENT_VIGNETTE_URL), - toStringType(event.getVignetteUrl())); - handler.updateState(new ChannelUID(thingUid, group, CHANNEL_EVENT_SUBTYPE), + private void updateSubGroup(WebhookEvent event, String group) { + handler.updateState(group, CHANNEL_EVENT_TYPE, toStringType(event.getEventType())); + handler.updateState(group, CHANNEL_EVENT_TIME, toDateTimeType(event.getTime())); + handler.updateState(group, CHANNEL_EVENT_SNAPSHOT, toRawType(event.getSnapshotUrl())); + handler.updateState(group, CHANNEL_EVENT_SNAPSHOT_URL, toStringType(event.getSnapshotUrl())); + handler.updateState(group, CHANNEL_EVENT_VIGNETTE, toRawType(event.getVignetteUrl())); + handler.updateState(group, CHANNEL_EVENT_VIGNETTE_URL, toStringType(event.getVignetteUrl())); + handler.updateState(group, CHANNEL_EVENT_SUBTYPE, event.getSubTypeDescription().map(d -> toStringType(d)).orElse(UnDefType.NULL)); final String message = event.getName(); - handler.updateState(new ChannelUID(thingUid, group, CHANNEL_EVENT_MESSAGE), + handler.updateState(group, CHANNEL_EVENT_MESSAGE, message == null || message.isBlank() ? UnDefType.NULL : toStringType(message)); State personId = event.getPersons().isEmpty() ? UnDefType.NULL : toStringType(event.getPersons().values().iterator().next().getId()); @@ -161,4 +168,31 @@ public class CameraCapability extends HomeSecurityThingCapability { }); return result; } + + public @Nullable String ping(String vpnUrl) { + return getSecurityCapability().map(cap -> { + UriBuilder builder = UriBuilder.fromPath(cap.ping(vpnUrl)); + URI apiLocalUrl = null; + try { + apiLocalUrl = builder.build(); + if (apiLocalUrl.getHost().startsWith("169.254.")) { + logger.warn("Suspicious local IP address received: {}", apiLocalUrl); + Configuration config = handler.getThing().getConfiguration(); + if (config.containsKey(IP_ADDRESS)) { + String provided = (String) config.get(IP_ADDRESS); + apiLocalUrl = builder.host(provided).build(); + logger.info("Using {} as local url for '{}'", apiLocalUrl, thingUID); + } else { + logger.debug("No alternative ip Address provided, keeping API answer"); + } + } + } catch (UriBuilderException e) { // Crashed at first URI build + logger.warn("API returned a badly formatted local url address for '{}': {}", thingUID, e.getMessage()); + } catch (IllegalArgumentException e) { + logger.warn("Invalid fallback address provided in configuration for '{}' keeping API answer: {}", + thingUID, e.getMessage()); + } + return apiLocalUrl != null ? apiLocalUrl.toString() : null; + }).orElse(null); + } } diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/Capability.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/Capability.java index cea8018f4a..ec7eb2ea10 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/Capability.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/Capability.java @@ -36,6 +36,7 @@ import org.openhab.binding.netatmo.internal.api.dto.NAThing; import org.openhab.binding.netatmo.internal.api.dto.WebhookEvent; import org.openhab.binding.netatmo.internal.handler.CommonInterface; import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingUID; import org.openhab.core.thing.binding.ThingHandlerService; import org.openhab.core.types.Command; @@ -50,6 +51,7 @@ public class Capability { protected final Thing thing; protected final CommonInterface handler; protected final ModuleType moduleType; + protected final ThingUID thingUID; protected boolean firstLaunch; protected Map properties = Map.of(); @@ -58,6 +60,7 @@ public class Capability { Capability(CommonInterface handler) { this.handler = handler; this.thing = handler.getThing(); + this.thingUID = thing.getUID(); this.moduleType = ModuleType.from(thing.getThingTypeUID()); } diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/EnergyCapability.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/EnergyCapability.java index e7ec8f9318..dad6236270 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/EnergyCapability.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/EnergyCapability.java @@ -59,7 +59,7 @@ public class EnergyCapability extends RestCapability { @Override public void initialize() { super.initialize(); - energyId = handler.getConfiguration().as(HomeConfiguration.class).getIdForArea(FeatureArea.ENERGY); + energyId = handler.getThingConfigAs(HomeConfiguration.class).getIdForArea(FeatureArea.ENERGY); } @Override @@ -77,7 +77,7 @@ public class EnergyCapability extends RestCapability { .forEach(bridgedModule -> childHandler.setNewData(bridgedModule)); }); }); - descriptionProvider.setStateOptions(new ChannelUID(thing.getUID(), GROUP_ENERGY, CHANNEL_PLANNING), + descriptionProvider.setStateOptions(new ChannelUID(thingUID, GROUP_ENERGY, CHANNEL_PLANNING), energyData.getThermSchedules().stream().map(p -> new StateOption(p.getId(), p.getName())).toList()); setPointDefaultDuration = energyData.getThermSetpointDefaultDuration(); } diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/HomeCapability.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/HomeCapability.java index cc610ca641..a72f6aaf68 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/HomeCapability.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/HomeCapability.java @@ -58,7 +58,7 @@ public class HomeCapability extends RestCapability { @Override public void initialize() { super.initialize(); - HomeConfiguration config = handler.getConfiguration().as(HomeConfiguration.class); + HomeConfiguration config = handler.getThingConfigAs(HomeConfiguration.class); homeIds.add(config.getId()); if (!config.energyId.isBlank()) { homeIds.add(config.energyId); diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/PersonCapability.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/PersonCapability.java index c09554a4f2..5f337af895 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/PersonCapability.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/PersonCapability.java @@ -34,7 +34,6 @@ import org.openhab.binding.netatmo.internal.handler.channelhelper.ChannelHelper; import org.openhab.binding.netatmo.internal.providers.NetatmoDescriptionProvider; import org.openhab.core.library.types.OnOffType; import org.openhab.core.thing.ChannelUID; -import org.openhab.core.thing.ThingUID; import org.openhab.core.types.Command; import org.openhab.core.types.StateOption; import org.openhab.core.types.UnDefType; @@ -53,7 +52,7 @@ public class PersonCapability extends HomeSecurityThingCapability { public PersonCapability(CommonInterface handler, NetatmoDescriptionProvider descriptionProvider, List channelHelpers) { super(handler, descriptionProvider, channelHelpers); - this.cameraChannelUID = new ChannelUID(thing.getUID(), GROUP_PERSON_LAST_EVENT, CHANNEL_EVENT_CAMERA_ID); + this.cameraChannelUID = new ChannelUID(thingUID, GROUP_PERSON_LAST_EVENT, CHANNEL_EVENT_CAMERA_ID); } @Override @@ -78,21 +77,15 @@ public class PersonCapability extends HomeSecurityThingCapability { protected void updateWebhookEvent(WebhookEvent event) { super.updateWebhookEvent(event); - ThingUID thingUid = thing.getUID(); - - handler.updateState(new ChannelUID(thingUid, GROUP_LAST_EVENT, CHANNEL_EVENT_SUBTYPE), + handler.updateState(GROUP_LAST_EVENT, CHANNEL_EVENT_SUBTYPE, event.getSubTypeDescription().map(d -> toStringType(d)).orElse(UnDefType.NULL)); final String message = event.getName(); - handler.updateState(new ChannelUID(thingUid, GROUP_LAST_EVENT, CHANNEL_EVENT_MESSAGE), + handler.updateState(GROUP_LAST_EVENT, CHANNEL_EVENT_MESSAGE, message == null || message.isBlank() ? UnDefType.NULL : toStringType(message)); - handler.updateState(new ChannelUID(thingUid, GROUP_LAST_EVENT, CHANNEL_EVENT_TIME), - toDateTimeType(event.getTime())); - - handler.updateState(new ChannelUID(thingUid, GROUP_LAST_EVENT, CHANNEL_EVENT_SNAPSHOT), - toRawType(event.getSnapshotUrl())); - + handler.updateState(GROUP_LAST_EVENT, CHANNEL_EVENT_TIME, toDateTimeType(event.getTime())); + handler.updateState(GROUP_LAST_EVENT, CHANNEL_EVENT_SNAPSHOT, toRawType(event.getSnapshotUrl())); handler.updateState(cameraChannelUID, toStringType(event.getCameraId())); } diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/SecurityCapability.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/SecurityCapability.java index 3ab10abc13..624606606a 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/SecurityCapability.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/SecurityCapability.java @@ -65,7 +65,7 @@ class SecurityCapability extends RestCapability { public void initialize() { super.initialize(); freshestEventTime = null; - securityId = handler.getConfiguration().as(HomeConfiguration.class).getIdForArea(FeatureArea.SECURITY); + securityId = handler.getThingConfigAs(HomeConfiguration.class).getIdForArea(FeatureArea.SECURITY); } @Override diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/channelhelper/CameraChannelHelper.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/channelhelper/CameraChannelHelper.java index 5d207abbd2..5e63cf85d7 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/channelhelper/CameraChannelHelper.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/channelhelper/CameraChannelHelper.java @@ -85,7 +85,7 @@ public class CameraChannelHelper extends ChannelHelper { if (!isMonitoring || (local && !isLocal) || url == null) { return null; } - return String.format("%s%s", url, LIVE_PICTURE); + return "%s%s".formatted(url, LIVE_PICTURE); } private State getLiveStreamURL(boolean local, @Nullable String configQual, boolean isMonitoring) { @@ -94,6 +94,6 @@ public class CameraChannelHelper extends ChannelHelper { return UnDefType.NULL; } String finalQual = configQual != null ? configQual : "poor"; - return toStringType("%s/live/%s", url, local ? String.format("files/%s/index.m3u8", finalQual) : "index.m3u8"); + return toStringType("%s/live/%s", url, local ? "files/%s/index.m3u8".formatted(finalQual) : "index.m3u8"); } } diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/providers/NetatmoThingTypeProvider.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/providers/NetatmoThingTypeProvider.java index fc61d4b2ab..bf2079e66c 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/providers/NetatmoThingTypeProvider.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/providers/NetatmoThingTypeProvider.java @@ -77,7 +77,7 @@ public class NetatmoThingTypeProvider implements ThingTypeProvider { .withExtensibleChannelTypeIds(moduleType.getExtensions()) .withChannelGroupDefinitions(getGroupDefinitions(moduleType)) .withProperties(Map.of(PROPERTY_THING_TYPE_VERSION, moduleType.thingTypeVersion)) - .withConfigDescriptionURI(moduleType.getConfigDescription()); + .withConfigDescriptionURI(moduleType.configDescription); ThingTypeUID bridgeType = moduleType.getBridge().thingTypeUID; if (!ModuleType.UNKNOWN.thingTypeUID.equals(bridgeType)) { diff --git a/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/config/config.xml b/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/config/config.xml index 808d58f4b1..f1d88a60ec 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/config/config.xml +++ b/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/config/config.xml @@ -103,6 +103,18 @@ + + + + @text/config.equipmentId.description + + + + @text/config.ipAddress.description + network-address + + + diff --git a/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/i18n/netatmo.properties b/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/i18n/netatmo.properties index 40aac14ddb..940b2d6f69 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/i18n/netatmo.properties +++ b/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/i18n/netatmo.properties @@ -444,6 +444,8 @@ config.reconnectInterval.label = Reconnect Interval config.reconnectInterval.description = The reconnection interval to Netatmo API (in s). config.equipmentId.label = Equipment ID config.equipmentId.description = ID of the device (MAC address). +config.ipAddress.label = Network Address +config.ipAddress.description = The IP or host name used as fallback in case of erroneous API answer to ping requests. config.thingId.label = Thing ID config.thingId.description = Unique identifier of the thing defined by Netatmo. config.securityId.label = Security ID