Skip to content

Commit

Permalink
[netatmo] Fix erroneous local URL handling (openhab#16492)
Browse files Browse the repository at this point in the history
* 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 <gael@lhopital.org>
Signed-off-by: gael@lhopital.org <gael@lhopital.org>
  • Loading branch information
clinique authored and joni1993 committed Oct 15, 2024
1 parent 397d925 commit 2ae3d89
Show file tree
Hide file tree
Showing 14 changed files with 136 additions and 93 deletions.
12 changes: 6 additions & 6 deletions bundles/org.openhab.binding.netatmo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,92 +64,97 @@
*/
@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,
ChannelGroup.TEMP_INSIDE, ChannelGroup.MEASURE, ChannelGroup.TSTAMP_EXT,
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<ModuleType> AS_SET = EnumSet.allOf(ModuleType.class);

Expand All @@ -160,8 +165,9 @@ ChannelGroup.BATTERY_EXT, new ChannelGroup(Therm1ChannelHelper.class, GROUP_TYPE
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<Class<? extends Capability>> capabilities, ChannelGroup... channelGroups) {
this.bridgeType = Optional.ofNullable(bridge);
this.feature = feature;
Expand All @@ -170,6 +176,7 @@ ChannelGroup.BATTERY_EXT, new ChannelGroup(Therm1ChannelHelper.class, GROUP_TYPE
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() {
Expand Down Expand Up @@ -203,13 +210,6 @@ public ModuleType getBridge() {
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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
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;
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -108,11 +111,11 @@ default void expireData() {
}

default String getId() {
return getConfiguration().as(NAThingConfiguration.class).getId();
return getThingConfigAs(NAThingConfiguration.class).getId();
}

default Configuration getConfiguration() {
return getThing().getConfiguration();
default <T> T getThingConfigAs(Class<T> configurationClass) {
return getThing().getConfiguration().as(configurationClass);
}

default Stream<Channel> getActiveChannels() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@
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;

/**
Expand All @@ -45,15 +44,12 @@ public AlarmEventCapability(CommonInterface handler, NetatmoDescriptionProvider
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));
}

Expand Down
Loading

0 comments on commit 2ae3d89

Please sign in to comment.