Skip to content

Commit

Permalink
[netatmo] Fix Live Picture not always available (#16679)
Browse files Browse the repository at this point in the history
* Adressing issue on live picture

Signed-off-by: [email protected] <[email protected]>
Signed-off-by: root <[email protected]>
  • Loading branch information
clinique authored Apr 29, 2024
1 parent cf21184 commit 5f7282b
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 35 deletions.
3 changes: 2 additions & 1 deletion bundles/org.openhab.binding.netatmo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -528,7 +528,7 @@ Warnings:
| status | monitoring | Switch | Read-write | State of the camera (video surveillance on/off) |
| status | sd-card | String | Read-only | State of the SD card |
| status | alim | String | Read-only | State of the power connector |
| live | picture | Image | Read-only | Camera Live Snapshot |
| live | picture (**) | Image | Read-only | Camera Live Snapshot |
| live | local-picture-url | String | Read-only | Local Url of the live snapshot for this camera |
| live | vpn-picture-url | String | Read-only | Url of the live snapshot for this camera through Netatmo VPN. |
| live | local-stream-url (*) | String | Read-only | Local Url of the live stream for this camera (accessible if openhab server and camera are located on the same lan. |
Expand All @@ -547,6 +547,7 @@ Warnings:
| last-event | person-id | String | Read-only | Id of the person the event is about (if any) |

(*) This channel is configurable : low, poor, high.
(**) This channel handles the REFRESH command for on demand update.

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public ZonedDateTime getTime() {

@Override
public @Nullable String getPersonId() {
return persons.size() > 0 ? persons.keySet().iterator().next() : null;
return persons.isEmpty() ? null : persons.keySet().iterator().next();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.State;
import org.openhab.core.types.StateOption;
import org.openhab.core.types.UnDefType;
Expand Down Expand Up @@ -141,6 +142,9 @@ private void updateSubGroup(WebhookEvent event, String group) {
public void handleCommand(String channelName, Command command) {
if (command instanceof OnOffType && CHANNEL_MONITORING.equals(channelName)) {
getSecurityCapability().ifPresent(cap -> cap.changeStatus(localUrl, OnOffType.ON.equals(command)));
} else if (command instanceof RefreshType && CHANNEL_LIVEPICTURE.equals(channelName)) {
handler.updateState(GROUP_CAM_LIVE, CHANNEL_LIVEPICTURE,
toRawType(cameraHelper.getLivePictureURL(localUrl != null, true)));
} else {
super.handleCommand(channelName, command);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,64 +36,74 @@
public class CameraChannelHelper extends ChannelHelper {
private static final String QUALITY_CONF_ENTRY = "quality";
private static final String LIVE_PICTURE = "/live/snapshot_720.jpg";
private boolean isLocal;

private @Nullable String vpnUrl;
private @Nullable String localUrl;

public CameraChannelHelper(Set<String> providedGroups) {
super(providedGroups);
}

public void setUrls(String vpnUrl, @Nullable String localUrl) {
public void setUrls(@Nullable String vpnUrl, @Nullable String localUrl) {
this.localUrl = localUrl;
this.vpnUrl = vpnUrl;
this.isLocal = localUrl != null;
}

public @Nullable String getLocalURL() {
return localUrl;
}

@Override
protected @Nullable State internalGetProperty(String channelId, NAThing naThing, Configuration config) {
if (naThing instanceof HomeStatusModule camera) {
boolean isMonitoring = OnOffType.ON.equals(camera.getMonitoring());
switch (channelId) {
case CHANNEL_MONITORING:
return camera.getMonitoring();
case CHANNEL_SD_CARD:
return toStringType(camera.getSdStatus());
case CHANNEL_ALIM_STATUS:
return toStringType(camera.getAlimStatus());
case CHANNEL_LIVEPICTURE_VPN_URL:
return toStringType(getLivePictureURL(false, isMonitoring));
case CHANNEL_LIVEPICTURE_LOCAL_URL:
return toStringType(getLivePictureURL(true, isMonitoring));
case CHANNEL_LIVEPICTURE:
return toRawType(getLivePictureURL(isLocal, isMonitoring));
case CHANNEL_LIVESTREAM_VPN_URL:
return getLiveStreamURL(false, (String) config.get(QUALITY_CONF_ENTRY), isMonitoring);
case CHANNEL_LIVESTREAM_LOCAL_URL:
return getLiveStreamURL(true, (String) config.get(QUALITY_CONF_ENTRY), isMonitoring);
}
return switch (channelId) {
case CHANNEL_MONITORING -> camera.getMonitoring();
case CHANNEL_SD_CARD -> toStringType(camera.getSdStatus());
case CHANNEL_ALIM_STATUS -> toStringType(camera.getAlimStatus());
default -> liveChannels(channelId, (String) config.get(QUALITY_CONF_ENTRY), camera,
OnOffType.ON.equals(camera.getMonitoring()), localUrl != null);
};
}
return null;
}

private @Nullable String getLivePictureURL(boolean local, boolean isMonitoring) {
String url = local ? localUrl : vpnUrl;
if (!isMonitoring || (local && !isLocal) || url == null) {
public @Nullable String getLivePictureURL(boolean local, boolean isMonitoring) {
String url = getUrl(local);
if (!isMonitoring || url == null) {
return null;
}
return "%s%s".formatted(url, LIVE_PICTURE);
}

private @Nullable State liveChannels(String channelId, String qualityConf, HomeStatusModule camera,
boolean isMonitoring, boolean isLocal) {
if (vpnUrl == null) {
setUrls(camera.getVpnUrl(), localUrl);
}
return switch (channelId) {
case CHANNEL_LIVESTREAM_LOCAL_URL ->
isLocal ? getLiveStreamURL(true, qualityConf, isMonitoring) : UnDefType.NULL;
case CHANNEL_LIVEPICTURE_LOCAL_URL ->
isLocal ? toStringType(getLivePictureURL(true, isMonitoring)) : UnDefType.NULL;
case CHANNEL_LIVESTREAM_VPN_URL -> getLiveStreamURL(false, qualityConf, isMonitoring);
case CHANNEL_LIVEPICTURE_VPN_URL -> toStringType(getLivePictureURL(false, isMonitoring));
case CHANNEL_LIVEPICTURE -> {
State result = toRawType(getLivePictureURL(isLocal, isMonitoring));
if (UnDefType.NULL.equals(result) && isLocal) {// If local read is unsuccessfull, try the VPN version
result = toRawType(getLivePictureURL(false, isMonitoring));
}
yield result;
}
default -> null;
};
}

private @Nullable String getUrl(boolean local) {
return local ? localUrl : vpnUrl;
}

private State getLiveStreamURL(boolean local, @Nullable String configQual, boolean isMonitoring) {
String url = local ? localUrl : vpnUrl;
if (!isMonitoring || (local && !isLocal) || url == null) {
String url = getUrl(local);
if (!isMonitoring || url == null) {
return UnDefType.NULL;
}
String finalQual = configQual != null ? configQual : "poor";
return toStringType("%s/live/%s", url, local ? "files/%s/index.m3u8".formatted(finalQual) : "index.m3u8");
return toStringType("%s/live/%sindex.m3u8", url, local ? "files/%s/".formatted(finalQual) : "");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
*/
@NonNullByDefault
public class ChannelTypeUtils {
private static final int DEFAULT_TIMEOUT_MS = 30000;

public static @Nullable QuantityType<?> commandToQuantity(Command command, MeasureClass measureClass) {
Measure measureDef = measureClass.measureDefinition;
Expand Down Expand Up @@ -90,7 +91,8 @@ public static State toQuantityType(@Nullable Number value, Unit<?> unit) {

public static State toRawType(@Nullable String pictureUrl) {
if (pictureUrl != null) {
RawType picture = HttpUtil.downloadImage(pictureUrl);
// Retrieving local picture can be quite long then extend the timeout.
RawType picture = HttpUtil.downloadImage(pictureUrl, DEFAULT_TIMEOUT_MS);
if (picture != null) {
return picture;
}
Expand Down

0 comments on commit 5f7282b

Please sign in to comment.