mirror of
https://github.com/danieldemus/openhab-addons
synced 2026-07-29 12:34:21 +02:00
[ring] Add support for snapshots from camera devices (#20152)
* Initial framework to get Snapshot images Signed-off-by: Paul Smedley <paul@smedley.id.au>
This commit is contained in:
@@ -55,9 +55,13 @@ If hardware ID is not specified, the MAC address of the system running OpenHAB i
|
||||
|
||||
### Device Status (Video Doorbell Binding Thing, Stickup Cam Binding Thing, Other Binding Thing only):
|
||||
|
||||
| Channel Type ID | Item Type | Description |
|
||||
|------------------|-----------|---------------------|
|
||||
| battery | Number | Battery level in % |
|
||||
| Channel Type ID | Item Type | Description |
|
||||
|--------------------|-----------|-------------------------|
|
||||
| battery | Number | Battery level in % |
|
||||
| snapshot | Image | Image Snapshot * |
|
||||
| snapshot-timestamp | DateTime | Timestamp of Snapshot * |
|
||||
|
||||
*) Video Doorbell and Stickup Cam only
|
||||
|
||||
## Full Example
|
||||
|
||||
@@ -87,14 +91,18 @@ String RingEventKind "Ring Event Kind" { ch
|
||||
String RingEventDeviceID "Ring Device ID" { channel="ring:account:ringAccount:event#doorbotId" }
|
||||
String RingEventDeviceDescription "Ring Device Description" { channel="ring:account:ringAccount:event#doorbotDescription" }
|
||||
|
||||
Switch RingDoorbellEnabled "Ring Doorbell Polling Enabled" { channel="ring:doorbell:<ring_device_id>:control#enabled" }
|
||||
Number RingDoorbellBattery "Ring Doorbell Battery [%s]%" { channel="ring:doorbell:<ring_device_id>:status#battery"}
|
||||
Switch RingDoorbellEnabled "Ring Doorbell Polling Enabled" { channel="ring:doorbell:<ring_device_id>:control#enabled" }
|
||||
Number RingDoorbellBattery "Ring Doorbell Battery [%s]%" { channel="ring:doorbell:<ring_device_id>:status#battery"}
|
||||
Image RingDoorbellSnapshot "Ring Doorbell Snapshot" { channel="ring:doorbell:<ring_device_id>:status#snapshot"}
|
||||
DateTime RingDoorbellSnapshotTimeStamp "Ring Doorbell Snapshot Timestamp" { channel="ring:doorbell:<ring_device_id>:status#snapshot-timestamp"}
|
||||
|
||||
Switch RingChimeEnabled "Ring Chime Polling Enabled" { channel="ring:chime:<ring_device_id>:control#enabled" }
|
||||
|
||||
Switch RingStickupEnabled "Ring Stickup Polling Enabled" { channel="ring:stickupcam:<ring_device_id>:control#enabled" }
|
||||
Number RingStickupBattery "Ring Stickup Battery [%s]%" { channel="ring:stickupcam:<ring_device_id>:status#battery"}
|
||||
Switch RingStickupEnabled "Ring Stickup Polling Enabled" { channel="ring:stickupcam:<ring_device_id>:control#enabled" }
|
||||
Number RingStickupBattery "Ring Stickup Battery [%s]%" { channel="ring:stickupcam:<ring_device_id>:status#battery"}
|
||||
Image RingStickupSnapshot "Ring Stickup Snapshot" { channel="ring:stickupcam:<ring_device_id>:status#snapshot"}
|
||||
DateTime RingStickupSnapshotTimeStamp "Ring Stickup Snapshot Timestamp" { channel="ring:stickupcam:<ring_device_id>:status#snapshot-timestamp"}
|
||||
|
||||
Switch RingOtherEnabled "Ring Other Polling Enabled" { channel="ring:other:<ring_device_id>:control#enabled" }
|
||||
Number RingOtherBattery "Ring Other Battery [%s]%" { channel="ring:other:<ring_device_id>:status#battery"}
|
||||
Switch RingOtherEnabled "Ring Other Polling Enabled" { channel="ring:other:<ring_device_id>:control#enabled" }
|
||||
Number RingOtherBattery "Ring Other Battery [%s]%" { channel="ring:other:<ring_device_id>:status#battery"}
|
||||
```
|
||||
|
||||
+3
@@ -42,6 +42,9 @@ public class RingBindingConstants {
|
||||
|
||||
public static final String CHANNEL_STATUS_BATTERY = "status#battery";
|
||||
|
||||
public static final String CHANNEL_STATUS_SNAPSHOT = "status#snapshot";
|
||||
public static final String CHANNEL_STATUS_SNAPSHOT_TIMESTAMP = "status#snapshot-timestamp";
|
||||
|
||||
public static final String CHANNEL_EVENT_URL = "event#url";
|
||||
public static final String CHANNEL_EVENT_CREATED_AT = "event#createdAt";
|
||||
public static final String CHANNEL_EVENT_KIND = "event#kind";
|
||||
|
||||
+2
@@ -34,6 +34,8 @@ public class ApiConstants {
|
||||
public static final String URL_RECORDING_END = "/share/play?disable_redirect=true";
|
||||
public static final String URL_DOORBELLS = API_BASE + "/clients_api/doorbots";
|
||||
public static final String URL_CHIMES = API_BASE + "/clients_api/chimes";
|
||||
public static final String URL_SNAPSHOT_TIMESTAMPS = API_BASE + "/clients_api/snapshots/timestamps";
|
||||
public static final String URL_SNAPSHOTS = API_BASE + "/clients_api/snapshots/image/";
|
||||
|
||||
public static final String URL_RECORDING = "/clients_api/dings/{0}/recording";
|
||||
|
||||
|
||||
+43
-1
@@ -46,6 +46,7 @@ import org.openhab.binding.ring.internal.api.ProfileTO;
|
||||
import org.openhab.binding.ring.internal.api.RingDevicesTO;
|
||||
import org.openhab.binding.ring.internal.api.RingEventTO;
|
||||
import org.openhab.binding.ring.internal.api.SessionTO;
|
||||
import org.openhab.binding.ring.internal.api.SessionTimestampTO;
|
||||
import org.openhab.binding.ring.internal.api.TokenTO;
|
||||
import org.openhab.binding.ring.internal.data.ParamBuilder;
|
||||
import org.openhab.binding.ring.internal.data.Tokens;
|
||||
@@ -237,7 +238,48 @@ public class RestClient {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get get the Ring devices
|
||||
* Get the timestamp of the last camera snapshot
|
||||
*
|
||||
* @param id the device id of the Ring cameras
|
||||
* @return a long of the timestamp of the last snapsnot
|
||||
* @throws AuthenticationException when request is invalid.
|
||||
*/
|
||||
public long getSnapshotTimestamp(String deviceId, Tokens tokens) throws AuthenticationException {
|
||||
String input = "{\"doorbot_ids\":[" + deviceId + "]}";
|
||||
String jsonResult = postRequest(ApiConstants.URL_SNAPSHOT_TIMESTAMPS, input, Map.of(), tokens);
|
||||
SessionTimestampTO sessionTimestamp = Objects
|
||||
.requireNonNull(gson.fromJson(jsonResult, SessionTimestampTO.class));
|
||||
if (sessionTimestamp.data.length > 0) {
|
||||
return sessionTimestamp.data[0].timestamp;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the image from the camera
|
||||
*
|
||||
* @param id the device id of the Ring cameras
|
||||
* @return a byte array of the camera image
|
||||
* @throws AuthenticationException when request is invalid.
|
||||
*/
|
||||
public byte[] getSnapshot(String deviceId, Tokens tokens) throws AuthenticationException {
|
||||
try {
|
||||
ContentResponse response = httpClient.newRequest(ApiConstants.URL_SNAPSHOTS + deviceId)
|
||||
.header(HttpHeader.AUTHORIZATION.asString(), "Bearer " + tokens.accessToken()).send();
|
||||
|
||||
if (response.getStatus() == 200) {
|
||||
return response.getContent();
|
||||
} else {
|
||||
throw new AuthenticationException("Failed to download snapshot: " + response.getStatus());
|
||||
}
|
||||
} catch (ExecutionException | InterruptedException | TimeoutException e) {
|
||||
throw new AuthenticationException("Failed to download snapshot.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Ring devices
|
||||
*
|
||||
* @param tokens the tokens previously retrieved when authenticating.
|
||||
* @return the RingDevices instance filled with all available data.
|
||||
|
||||
+18
@@ -31,4 +31,22 @@ public interface RingAccount {
|
||||
|
||||
@Nullable
|
||||
RingDevice getDevice(String id);
|
||||
|
||||
/**
|
||||
* Get the timestamp of the last camera snapshot
|
||||
*
|
||||
* @param id the device id of the Ring cameras
|
||||
* @return a long of the timestamp of the last snapsnot
|
||||
* @throws AuthenticationException when request is invalid.
|
||||
*/
|
||||
long getSnapshotTimestamp(String id);
|
||||
|
||||
/**
|
||||
* Get the image from the camera
|
||||
*
|
||||
* @param id the device id of the Ring cameras
|
||||
* @return a byte array of the camera image
|
||||
* @throws AuthenticationException when request is invalid.
|
||||
*/
|
||||
byte[] getSnapshot(String id);
|
||||
}
|
||||
|
||||
+6
-3
@@ -24,6 +24,7 @@ import org.openhab.binding.ring.internal.handler.ChimeHandler;
|
||||
import org.openhab.binding.ring.internal.handler.DoorbellHandler;
|
||||
import org.openhab.binding.ring.internal.handler.OtherDeviceHandler;
|
||||
import org.openhab.binding.ring.internal.handler.StickupcamHandler;
|
||||
import org.openhab.core.i18n.TimeZoneProvider;
|
||||
import org.openhab.core.io.net.http.HttpClientFactory;
|
||||
import org.openhab.core.net.HttpServiceUtil;
|
||||
import org.openhab.core.net.NetworkAddressService;
|
||||
@@ -62,6 +63,7 @@ public class RingHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
private final HttpClient httpClient;
|
||||
private final RingVideoServlet servlet;
|
||||
private TimeZoneProvider timeZoneProvider;
|
||||
private int httpPort;
|
||||
|
||||
public final Gson gson = new Gson();
|
||||
@@ -69,7 +71,7 @@ public class RingHandlerFactory extends BaseThingHandlerFactory {
|
||||
@Activate
|
||||
public RingHandlerFactory(@Reference NetworkAddressService networkAddressService,
|
||||
@Reference RingVideoServlet servlet, @Reference HttpClientFactory httpClientFactory,
|
||||
ComponentContext componentContext) throws Exception {
|
||||
@Reference TimeZoneProvider timeZoneProvider, ComponentContext componentContext) throws Exception {
|
||||
super.activate(componentContext);
|
||||
httpPort = HttpServiceUtil.getHttpServicePort(componentContext.getBundleContext());
|
||||
if (httpPort == -1) {
|
||||
@@ -77,6 +79,7 @@ public class RingHandlerFactory extends BaseThingHandlerFactory {
|
||||
}
|
||||
this.servlet = servlet;
|
||||
this.networkAddressService = networkAddressService;
|
||||
this.timeZoneProvider = timeZoneProvider;
|
||||
|
||||
logger.debug("Using OH HTTP port {}", httpPort);
|
||||
|
||||
@@ -107,11 +110,11 @@ public class RingHandlerFactory extends BaseThingHandlerFactory {
|
||||
return null;
|
||||
}
|
||||
} else if (thingTypeUID.equals(THING_TYPE_DOORBELL)) {
|
||||
return new DoorbellHandler(thing);
|
||||
return new DoorbellHandler(thing, timeZoneProvider);
|
||||
} else if (thingTypeUID.equals(THING_TYPE_CHIME)) {
|
||||
return new ChimeHandler(thing);
|
||||
} else if (thingTypeUID.equals(THING_TYPE_STICKUPCAM)) {
|
||||
return new StickupcamHandler(thing);
|
||||
return new StickupcamHandler(thing, timeZoneProvider);
|
||||
} else if (thingTypeUID.equals(THING_TYPE_OTHERDEVICE)) {
|
||||
return new OtherDeviceHandler(thing);
|
||||
}
|
||||
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2026 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.ring.internal.api;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* @author Paul Smedley - Initial contribution
|
||||
*/
|
||||
|
||||
@NonNullByDefault
|
||||
public class SessionTimestampTO {
|
||||
@SerializedName("timestamps")
|
||||
public @NonNullByDefault({}) Data[] data;
|
||||
|
||||
public static class Data {
|
||||
public int doorbot_id;
|
||||
public long timestamp;
|
||||
}
|
||||
}
|
||||
+25
-6
@@ -281,9 +281,8 @@ public class AccountHandler extends BaseBridgeHandler implements RingAccount {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, ex.getMessage());
|
||||
}
|
||||
} catch (JsonParseException e) {
|
||||
logger.debug("Invalid response from api.ring.com when initializing Ring Account handler{}", e.getMessage());
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"Invalid response from api.ring.com");
|
||||
"@text/offline.comm-error.invalid-response");
|
||||
}
|
||||
logger.debug("doLogin RT: {}", getRefreshTokenFromFile());
|
||||
try {
|
||||
@@ -291,12 +290,10 @@ public class AccountHandler extends BaseBridgeHandler implements RingAccount {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
} catch (AuthenticationException ae) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"AuthenticationException response from ring.com");
|
||||
logger.debug("RestClient reported AuthenticationException in finally block: {}", ae.getMessage());
|
||||
"@text/offline.comm-error.auth-exception");
|
||||
} catch (JsonParseException pe1) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"JsonParseException response from ring.com");
|
||||
logger.debug("RestClient reported JsonParseException in finally block: {}", pe1.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -549,7 +546,7 @@ public class AccountHandler extends BaseBridgeHandler implements RingAccount {
|
||||
stopSessionRefresh();
|
||||
stopAutomaticRefresh();
|
||||
ExecutorService service = this.videoExecutorService;
|
||||
if (service != null) {
|
||||
if (!service.isShutdown()) {
|
||||
service.shutdownNow();
|
||||
}
|
||||
this.videoExecutorService = null;
|
||||
@@ -567,6 +564,28 @@ public class AccountHandler extends BaseBridgeHandler implements RingAccount {
|
||||
return registry.getRingDevice(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSnapshotTimestamp(String id) {
|
||||
try {
|
||||
return restClient.getSnapshotTimestamp(id, tokens);
|
||||
} catch (AuthenticationException ae) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"@text/offline.comm-error.invalid-response");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getSnapshot(String id) {
|
||||
try {
|
||||
return restClient.getSnapshot(id, tokens);
|
||||
} catch (AuthenticationException ae) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"@text/offline.comm-error.auth-exception");
|
||||
}
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Class<? extends ThingHandlerService>> getServices() {
|
||||
return Set.of(RingDiscoveryService.class);
|
||||
|
||||
+22
-1
@@ -13,11 +13,18 @@
|
||||
package org.openhab.binding.ring.internal.handler;
|
||||
|
||||
import static org.openhab.binding.ring.RingBindingConstants.CHANNEL_STATUS_BATTERY;
|
||||
import static org.openhab.binding.ring.RingBindingConstants.CHANNEL_STATUS_SNAPSHOT;
|
||||
import static org.openhab.binding.ring.RingBindingConstants.CHANNEL_STATUS_SNAPSHOT_TIMESTAMP;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.ring.internal.api.RingDeviceTO;
|
||||
import org.openhab.binding.ring.internal.device.Doorbell;
|
||||
import org.openhab.core.i18n.TimeZoneProvider;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.RawType;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.types.Command;
|
||||
@@ -33,9 +40,12 @@ import org.openhab.core.types.Command;
|
||||
@NonNullByDefault
|
||||
public class DoorbellHandler extends RingDeviceHandler {
|
||||
private int lastBattery = -1;
|
||||
private long lastSnapshotTimestamp = -1;
|
||||
private TimeZoneProvider timeZoneProvider;
|
||||
|
||||
public DoorbellHandler(Thing thing) {
|
||||
public DoorbellHandler(Thing thing, TimeZoneProvider timeZoneProvider) {
|
||||
super(thing);
|
||||
this.timeZoneProvider = timeZoneProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -70,5 +80,16 @@ public class DoorbellHandler extends RingDeviceHandler {
|
||||
logger.debug("Battery Level Unchanged for {} - {} vs {}", getThing().getUID().getId(),
|
||||
deviceTO.health.batteryPercentage, lastBattery);
|
||||
}
|
||||
long timestamp = getSnapshotTimestamp();
|
||||
if (timestamp > lastSnapshotTimestamp) {
|
||||
logger.debug("timestamp = {} != lastSnapshotTimestamp {}, update snapshot channel", timestamp,
|
||||
lastSnapshotTimestamp);
|
||||
lastSnapshotTimestamp = timestamp;
|
||||
ChannelUID channelUID = new ChannelUID(thing.getUID(), CHANNEL_STATUS_SNAPSHOT);
|
||||
updateState(channelUID, new RawType(getSnapshot(), "image/jpeg"));
|
||||
channelUID = new ChannelUID(thing.getUID(), CHANNEL_STATUS_SNAPSHOT_TIMESTAMP);
|
||||
updateState(channelUID, new DateTimeType(ZonedDateTime.ofInstant(java.time.Instant.ofEpochMilli(timestamp),
|
||||
timeZoneProvider.getTimeZone())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+20
-5
@@ -31,7 +31,6 @@ import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.binding.BridgeHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
|
||||
@@ -79,16 +78,32 @@ public abstract class RingDeviceHandler extends AbstractRingHandler {
|
||||
}
|
||||
|
||||
protected @Nullable RingDevice getDevice() {
|
||||
Bridge bridge = getBridge();
|
||||
if (bridge != null) {
|
||||
BridgeHandler bridgeHandler = bridge.getHandler();
|
||||
if (bridgeHandler instanceof RingAccount ringAccount) {
|
||||
if (getBridge() instanceof Bridge bridge) {
|
||||
if (bridge.getHandler() instanceof RingAccount ringAccount) {
|
||||
return ringAccount.getDevice(config.id);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected long getSnapshotTimestamp() {
|
||||
if (getBridge() instanceof Bridge bridge) {
|
||||
if (bridge.getHandler() instanceof RingAccount ringAccount) {
|
||||
return ringAccount.getSnapshotTimestamp(config.id);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
protected byte[] getSnapshot() {
|
||||
if (getBridge() instanceof Bridge bridge) {
|
||||
if (bridge.getHandler() instanceof RingAccount ringAccount) {
|
||||
return ringAccount.getSnapshot(config.id);
|
||||
}
|
||||
}
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Link the device, and update the device with the status CONFIGURED.
|
||||
*
|
||||
|
||||
+23
-1
@@ -13,11 +13,18 @@
|
||||
package org.openhab.binding.ring.internal.handler;
|
||||
|
||||
import static org.openhab.binding.ring.RingBindingConstants.CHANNEL_STATUS_BATTERY;
|
||||
import static org.openhab.binding.ring.RingBindingConstants.CHANNEL_STATUS_SNAPSHOT;
|
||||
import static org.openhab.binding.ring.RingBindingConstants.CHANNEL_STATUS_SNAPSHOT_TIMESTAMP;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.ring.internal.api.RingDeviceTO;
|
||||
import org.openhab.binding.ring.internal.device.Stickupcam;
|
||||
import org.openhab.core.i18n.TimeZoneProvider;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.RawType;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.types.Command;
|
||||
@@ -33,9 +40,12 @@ import org.openhab.core.types.Command;
|
||||
@NonNullByDefault
|
||||
public class StickupcamHandler extends RingDeviceHandler {
|
||||
private int lastBattery = -1;
|
||||
private long lastSnapshotTimestamp = -1;
|
||||
private TimeZoneProvider timeZoneProvider;
|
||||
|
||||
public StickupcamHandler(Thing thing) {
|
||||
public StickupcamHandler(Thing thing, TimeZoneProvider timeZoneProvider) {
|
||||
super(thing);
|
||||
this.timeZoneProvider = timeZoneProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -71,5 +81,17 @@ public class StickupcamHandler extends RingDeviceHandler {
|
||||
deviceTO.health.batteryPercentage, lastBattery);
|
||||
|
||||
}
|
||||
|
||||
long timestamp = getSnapshotTimestamp();
|
||||
if (timestamp > lastSnapshotTimestamp) {
|
||||
logger.debug("timestamp = {} != lastSnapshotTimestamp {}, update snapshot channel", timestamp,
|
||||
lastSnapshotTimestamp);
|
||||
lastSnapshotTimestamp = timestamp;
|
||||
ChannelUID channelUID = new ChannelUID(thing.getUID(), CHANNEL_STATUS_SNAPSHOT);
|
||||
updateState(channelUID, new RawType(getSnapshot(), "image/jpeg"));
|
||||
channelUID = new ChannelUID(thing.getUID(), CHANNEL_STATUS_SNAPSHOT_TIMESTAMP);
|
||||
updateState(channelUID, new DateTimeType(ZonedDateTime.ofInstant(java.time.Instant.ofEpochMilli(timestamp),
|
||||
timeZoneProvider.getTimeZone())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,5 +83,15 @@ channel-type.ring.enabled.label = Enable Polling
|
||||
channel-type.ring.enabled.description = Account Polling Enabled (on=yes, off=no)
|
||||
channel-type.ring.kind.label = Event Type
|
||||
channel-type.ring.kind.description = The kind of event, usually 'motion' or 'ding'
|
||||
channel-type.ring.snapshot-timestamp.label = Snapshot DateTime
|
||||
channel-type.ring.snapshot-timestamp.description = The date and time the snapshot was created
|
||||
channel-type.ring.snapshot-timestamp.state.pattern = %1$tF %1$tR
|
||||
channel-type.ring.snapshot.label = Snapshot
|
||||
channel-type.ring.snapshot.description = Snapshot image from camera
|
||||
channel-type.ring.url.label = URL to recorded video
|
||||
channel-type.ring.url.description = The URL to a recorded video (only when subscribed)
|
||||
|
||||
# thing status descriptions
|
||||
|
||||
offline.comm-error.invalid-response = Invalid response from api.ring.com
|
||||
offline.comm-error.auth-exception = Authentication issue from api.ring.com
|
||||
|
||||
@@ -126,6 +126,9 @@
|
||||
<channel-group id="control" typeId="controlGroup"/>
|
||||
<channel-group id="status" typeId="deviceStatus"/>
|
||||
</channel-groups>
|
||||
<properties>
|
||||
<property name="thingTypeVersion">1</property>
|
||||
</properties>
|
||||
<representation-property>id</representation-property>
|
||||
<config-description>
|
||||
<parameter name="id" type="text">
|
||||
@@ -155,6 +158,9 @@
|
||||
<channel-group id="control" typeId="controlGroup"/>
|
||||
<channel-group id="status" typeId="deviceStatus"/>
|
||||
</channel-groups>
|
||||
<properties>
|
||||
<property name="thingTypeVersion">1</property>
|
||||
</properties>
|
||||
<representation-property>id</representation-property>
|
||||
<config-description>
|
||||
<parameter name="id" type="text">
|
||||
@@ -188,6 +194,8 @@
|
||||
<description>Device Status Information</description>
|
||||
<channels>
|
||||
<channel id="battery" typeId="battery"/>
|
||||
<channel id="snapshot" typeId="snapshot"/>
|
||||
<channel id="snapshot-timestamp" typeId="snapshot-timestamp"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
@@ -269,4 +277,24 @@
|
||||
</tags>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
<channel-type id="snapshot">
|
||||
<item-type>Image</item-type>
|
||||
<label>Snapshot</label>
|
||||
<description>Snapshot image from camera</description>
|
||||
<tags>
|
||||
<tag>Status</tag>
|
||||
<tag>Info</tag>
|
||||
</tags>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
<channel-type id="snapshot-timestamp">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Snapshot DateTime</label>
|
||||
<description>The date and time the snapshot was created</description>
|
||||
<tags>
|
||||
<tag>Status</tag>
|
||||
<tag>Timestamp</tag>
|
||||
</tags>
|
||||
<state pattern="%1$tF %1$tR" readOnly="true"/>
|
||||
</channel-type>
|
||||
</thing:thing-descriptions>
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||
<update:update-descriptions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:update="https://openhab.org/schemas/update-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/update-description/v1.0.0 https://openhab.org/schemas/update-description-1.0.0.xsd">
|
||||
|
||||
<thing-type uid="ring:stickupcam">
|
||||
<instruction-set targetVersion="1">
|
||||
<add-channel id="snapshot" groupIds="status">
|
||||
<type>ring:snapshot</type>
|
||||
</add-channel>
|
||||
<add-channel id="snapshot-timestamp" groupIds="status">
|
||||
<type>ring:snapshot-timestamp</type>
|
||||
</add-channel>
|
||||
</instruction-set>
|
||||
</thing-type>
|
||||
|
||||
<thing-type uid="ring:doorbell">
|
||||
<instruction-set targetVersion="1">
|
||||
<add-channel id="snapshot" groupIds="status">
|
||||
<type>ring:snapshot</type>
|
||||
</add-channel>
|
||||
<add-channel id="snapshot-timestamp" groupIds="status">
|
||||
<type>ring:snapshot-timestamp</type>
|
||||
</add-channel>
|
||||
</instruction-set>
|
||||
</thing-type>
|
||||
|
||||
</update:update-descriptions>
|
||||
Reference in New Issue
Block a user