Skip to content

Commit

Permalink
[netatmo] Null annotations Part 3 of 3 (openhab#8057)
Browse files Browse the repository at this point in the history
* [netatmo] Null annotations Part 3 of 3
Fix openhab#7913
* Video status only set when there is a video in the event
* Add a setting to enable/disable the background discovery
Fix openhab#8083
* Cache Optional result in local variable

Signed-off-by: Laurent Garnier <lg.hc@free.fr>
  • Loading branch information
lolodomo authored and andrewfg committed Aug 31, 2020
1 parent 4792624 commit e3e9951
Show file tree
Hide file tree
Showing 24 changed files with 793 additions and 766 deletions.
20 changes: 12 additions & 8 deletions bundles/org.openhab.binding.netatmo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@ See http://www.netatmo.com/ for details on their product.

## Binding Configuration

The binding has no configuration options itself, all configuration is done at 'Things' level but before, you will have to grant openHAB to access Netatmo API.
The binding has the following configuration options:

| Parameter | Name | Description |
|---------------------|----------------------|-----------------------------------|
| backgroundDiscovery | Background Discovery | If set to true, the device and its associated modules are updated in the discovery inbox at each API call run to refresh device data. Default is false. |

Before setting up your 'Things', you will have to grant openHAB to access Netatmo API.
Here is the procedure:

### 1. Application Creation
Expand Down Expand Up @@ -503,10 +509,9 @@ All these channels are read only.
### Welcome and Presence Camera

Warnings:
- The URL of the live snapshot is a fixed URL so the value of the channel cameraLivePictureUrl / welcomeCameraLivePictureUrl will never be updated once first set by the binding.
So to get a refreshed picture, you need to use the refresh parameter in your sitemap image element.
- Some features like the video surveillance are accessed via the local network, so it may be helpful to set a static IP address
for the camera within your local network.

- The URL of the live snapshot is a fixed URL so the value of the channel cameraLivePictureUrl / welcomeCameraLivePictureUrl will never be updated once first set by the binding. So to get a refreshed picture, you need to use the refresh parameter in your sitemap image element.
- Some features like the video surveillance are accessed via the local network, so it may be helpful to set a static IP address for the camera within your local network.

**Supported channels for the Welcome Camera thing:**

Expand All @@ -523,9 +528,8 @@ for the camera within your local network.
**Supported channels for the Presence Camera thing:**

Warnings:
- The floodlight auto-mode (cameraFloodlightAutoMode) isn't updated it is changed by another application. Therefore the
binding handles its own state of the auto-mode. This has the advantage that the user can define its own floodlight
switch off behaviour.

- The floodlight auto-mode (cameraFloodlightAutoMode) isn't updated it is changed by another application. Therefore the binding handles its own state of the auto-mode. This has the advantage that the user can define its own floodlight switch off behaviour.

| Channel ID | Item Type | Read/Write | Description |
|-----------------------------|-----------|------------|--------------------------------------------------------------|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import static org.openhab.binding.netatmo.internal.NetatmoBindingConstants.*;

import java.util.Dictionary;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
Expand Down Expand Up @@ -47,6 +48,7 @@
import org.openhab.binding.netatmo.internal.welcome.NAWelcomeHomeHandler;
import org.openhab.binding.netatmo.internal.welcome.NAWelcomePersonHandler;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
Expand All @@ -69,6 +71,7 @@ public class NetatmoHandlerFactory extends BaseThingHandlerFactory {
private final HttpService httpService;
private final NATherm1StateDescriptionProvider stateDescriptionProvider;
private final TimeZoneProvider timeZoneProvider;
private boolean backgroundDiscovery;

@Activate
public NetatmoHandlerFactory(final @Reference HttpService httpService,
Expand All @@ -79,6 +82,19 @@ public NetatmoHandlerFactory(final @Reference HttpService httpService,
this.timeZoneProvider = timeZoneProvider;
}

@Override
protected void activate(ComponentContext componentContext) {
super.activate(componentContext);
Dictionary<String, Object> properties = componentContext.getProperties();
Object property = properties.get("backgroundDiscovery");
if (property instanceof Boolean) {
backgroundDiscovery = ((Boolean) property).booleanValue();
} else {
backgroundDiscovery = false;
}
logger.debug("backgroundDiscovery {}", backgroundDiscovery);
}

@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return (SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID));
Expand Down Expand Up @@ -134,7 +150,10 @@ protected void removeHandler(ThingHandler thingHandler) {
private synchronized void registerDeviceDiscoveryService(NetatmoBridgeHandler netatmoBridgeHandler) {
if (bundleContext != null) {
NetatmoModuleDiscoveryService discoveryService = new NetatmoModuleDiscoveryService(netatmoBridgeHandler);
discoveryService.activate(null);
Map<String, @Nullable Object> configProperties = new HashMap<>();
configProperties.put(DiscoveryService.CONFIG_PROPERTY_BACKGROUND_DISCOVERY,
Boolean.valueOf(backgroundDiscovery));
discoveryService.activate(configProperties);
discoveryServiceRegs.put(netatmoBridgeHandler.getThing().getUID(), bundleContext
.registerService(DiscoveryService.class.getName(), discoveryService, new Hashtable<>()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@
*/
package org.openhab.binding.netatmo.internal.camera;

import java.util.Objects;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;

import java.util.Objects;

/**
* {@link CameraAddress} handles the data to address a camera (VPN and local address).
*
* @author Sven Strohschein
* @author Sven Strohschein - Initial contribution
*/
@NonNullByDefault
public class CameraAddress {
Expand All @@ -43,6 +43,7 @@ public String getLocalURL() {

/**
* Checks if the VPN URL was changed / isn't equal to the given VPN-URL.
*
* @param vpnURL old / known VPN URL
* @return true, when the VPN URL isn't equal given VPN URL, otherwise false
*/
Expand All @@ -52,8 +53,12 @@ public boolean isVpnURLChanged(String vpnURL) {

@Override
public boolean equals(@Nullable Object object) {
if (this == object) return true;
if (object == null || getClass() != object.getClass()) return false;
if (this == object) {
return true;
}
if (object == null || getClass() != object.getClass()) {
return false;
}
CameraAddress that = (CameraAddress) object;
return vpnURL.equals(that.vpnURL) && localURL.equals(that.localURL);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@
*/
package org.openhab.binding.netatmo.internal.camera;

import static org.openhab.binding.netatmo.internal.ChannelTypeUtils.*;
import static org.openhab.binding.netatmo.internal.ChannelTypeUtils.toOnOffType;
import static org.openhab.binding.netatmo.internal.NetatmoBindingConstants.*;

import org.eclipse.jdt.annotation.NonNull;
import java.io.IOException;
import java.util.Optional;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.smarthome.core.i18n.TimeZoneProvider;
import org.eclipse.smarthome.core.library.types.OnOffType;
import org.eclipse.smarthome.core.thing.ChannelUID;
Expand All @@ -29,19 +32,16 @@
import org.json.JSONObject;
import org.openhab.binding.netatmo.internal.ChannelTypeUtils;
import org.openhab.binding.netatmo.internal.handler.NetatmoModuleHandler;

import io.swagger.client.model.NAWelcomeCamera;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.Optional;
import io.swagger.client.model.NAWelcomeCamera;

/**
* {@link CameraHandler} is the class used to handle Camera Data
*
* @author Sven Strohschein (partly moved code from NAWelcomeCameraHandler to introduce inheritance, see
* NAWelcomeCameraHandler)
* @author Sven Strohschein - Initial contribution (partly moved code from NAWelcomeCameraHandler to introduce
* inheritance, see NAWelcomeCameraHandler)
*
*/
@NonNullByDefault
Expand All @@ -53,7 +53,7 @@ public abstract class CameraHandler extends NetatmoModuleHandler<NAWelcomeCamera

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

private Optional<CameraAddress> cameraAddress = Optional.empty();
private @Nullable CameraAddress cameraAddress;

protected CameraHandler(Thing thing, final TimeZoneProvider timeZoneProvider) {
super(thing, timeZoneProvider);
Expand All @@ -65,9 +65,9 @@ public void handleCommand(ChannelUID channelUID, Command command) {
switch (channelId) {
case CHANNEL_CAMERA_STATUS:
case CHANNEL_WELCOME_CAMERA_STATUS:
if(command == OnOffType.ON) {
if (command == OnOffType.ON) {
switchVideoSurveillance(true);
} else if(command == OnOffType.OFF) {
} else if (command == OnOffType.OFF) {
switchVideoSurveillance(false);
}
break;
Expand All @@ -81,7 +81,7 @@ protected void updateProperties(NAWelcomeCamera moduleData) {
}

@Override
protected State getNAThingProperty(@NonNull String channelId) {
protected State getNAThingProperty(String channelId) {
switch (channelId) {
case CHANNEL_CAMERA_STATUS:
return getStatusState();
Expand Down Expand Up @@ -188,7 +188,7 @@ private void switchVideoSurveillance(boolean isOn) {
Optional<String> localCameraURL = getLocalCameraURL();
if (localCameraURL.isPresent()) {
String url = localCameraURL.get() + STATUS_CHANGE_URL_PATH + "?status=";
if(isOn) {
if (isOn) {
url += "on";
} else {
url += "off";
Expand All @@ -201,17 +201,20 @@ private void switchVideoSurveillance(boolean isOn) {

protected Optional<String> getLocalCameraURL() {
Optional<String> vpnURLOptional = getVpnUrl();
CameraAddress address = cameraAddress;
if (vpnURLOptional.isPresent()) {
final String vpnURL = vpnURLOptional.get();

//The local address is (re-)requested when it wasn't already determined or when the vpn address was changed.
if (!cameraAddress.isPresent() || cameraAddress.get().isVpnURLChanged(vpnURL)) {
// The local address is (re-)requested when it wasn't already determined or when the vpn address was
// changed.
if (address == null || address.isVpnURLChanged(vpnURL)) {
Optional<JSONObject> json = executeGETRequestJSON(vpnURL + PING_URL_PATH);
cameraAddress = json.map(j -> j.optString("local_url", null))
.map(localURL -> new CameraAddress(vpnURL, localURL));
address = json.map(j -> j.optString("local_url", null))
.map(localURL -> new CameraAddress(vpnURL, localURL)).orElse(null);
cameraAddress = address;
}
}
return cameraAddress.map(CameraAddress::getLocalURL);
return Optional.ofNullable(address).map(CameraAddress::getLocalURL);
}

private Optional<JSONObject> executeGETRequestJSON(String url) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,9 @@
import org.openhab.binding.netatmo.internal.handler.NetatmoDataListener;

import io.swagger.client.model.NAHealthyHomeCoach;
import io.swagger.client.model.NAHealthyHomeCoachDataBody;
import io.swagger.client.model.NAMain;
import io.swagger.client.model.NAPlug;
import io.swagger.client.model.NAStationDataBody;
import io.swagger.client.model.NAThermostatDataBody;
import io.swagger.client.model.NAWelcomeHome;
import io.swagger.client.model.NAWelcomeHomeData;

/**
* The {@link NetatmoModuleDiscoveryService} searches for available Netatmo
Expand Down Expand Up @@ -70,42 +66,41 @@ public void deactivate() {
@Override
public void startScan() {
if (netatmoBridgeHandler.configuration.readStation) {
NAStationDataBody stationDataBody = netatmoBridgeHandler.getStationsDataBody(null);
if (stationDataBody != null) {
stationDataBody.getDevices().forEach(station -> {
netatmoBridgeHandler.getStationsDataBody(null).ifPresent(dataBody -> {
dataBody.getDevices().forEach(station -> {
discoverWeatherStation(station);
});
}
});
}
if (netatmoBridgeHandler.configuration.readHealthyHomeCoach) {
NAHealthyHomeCoachDataBody homecoachDataBody = netatmoBridgeHandler.getHomecoachDataBody(null);
if (homecoachDataBody != null) {
homecoachDataBody.getDevices().forEach(homecoach -> {
netatmoBridgeHandler.getHomecoachDataBody(null).ifPresent(dataBody -> {
dataBody.getDevices().forEach(homecoach -> {
discoverHomeCoach(homecoach);
});
}
});
}
if (netatmoBridgeHandler.configuration.readThermostat) {
NAThermostatDataBody thermostatsDataBody = netatmoBridgeHandler.getThermostatsDataBody(null);
if (thermostatsDataBody != null) {
thermostatsDataBody.getDevices().forEach(plug -> {
netatmoBridgeHandler.getThermostatsDataBody(null).ifPresent(dataBody -> {
dataBody.getDevices().forEach(plug -> {
discoverThermostat(plug);
});
}
});
}
if (netatmoBridgeHandler.configuration.readWelcome || netatmoBridgeHandler.configuration.readPresence) {
NAWelcomeHomeData welcomeHomeData = netatmoBridgeHandler.getWelcomeDataBody(null);
if (welcomeHomeData != null) {
welcomeHomeData.getHomes().forEach(home -> {
netatmoBridgeHandler.getWelcomeDataBody(null).ifPresent(dataBody -> {
dataBody.getHomes().forEach(home -> {
discoverWelcomeHome(home);
});
}
});
}
stopScan();
}

@Override
public void onDataRefreshed(Object data) {
if (!isBackgroundDiscoveryEnabled()) {
return;
}
if (data instanceof NAMain) {
discoverWeatherStation((NAMain) data);
} else if (data instanceof NAPlug) {
Expand Down
Loading

0 comments on commit e3e9951

Please sign in to comment.