[ring] Refactor discovery and allow multiple accounts (#18767)

With this refactoring the device registry is bound to the account bridge, making it possible to use multiple ring accounts. It also changes the discovery service to a `ThingHandlerService, simplifying code.

Signed-off-by: Jan N. Klug <github@klug.nrw>
pull/18778/head
J-N-K 2025-06-15 16:35:13 +02:00 committed by GitHub
parent 886c170631
commit 5437a6685c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 220 additions and 703 deletions

View File

@ -331,7 +331,7 @@
/bundles/org.openhab.binding.resol/ @ramack
/bundles/org.openhab.binding.revogi/ @openhab/add-ons-maintainers
/bundles/org.openhab.binding.rfxcom/ @martinvw @paulianttila
/bundles/org.openhab.binding.ring/ @morph166955
/bundles/org.openhab.binding.ring/ @psmedley
/bundles/org.openhab.binding.rme/ @kgoderis
/bundles/org.openhab.binding.robonect/ @reyem
/bundles/org.openhab.binding.roku/ @mlobstein

View File

@ -17,12 +17,13 @@ import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.ring.internal.RingDeviceRegistry;
import org.openhab.binding.ring.internal.RingAccount;
import org.openhab.binding.ring.internal.errors.DeviceNotFoundException;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.thing.binding.BridgeHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -104,15 +105,18 @@ public abstract class AbstractRingHandler extends BaseThingHandler {
@Override
public void handleRemoval() {
updateStatus(ThingStatus.OFFLINE);
final String id = getThing().getUID().getId();
final RingDeviceRegistry registry = RingDeviceRegistry.getInstance();
try {
registry.removeRingDevice(id);
} catch (final DeviceNotFoundException e) {
logger.debug("Exception occurred during execution of handleRemoval(): {}", e.getMessage(), e);
} finally {
updateStatus(ThingStatus.REMOVED);
String id = getThing().getUID().getId();
Bridge bridge = getBridge();
if (bridge != null) {
BridgeHandler bridgeHandler = bridge.getHandler();
if (bridgeHandler instanceof RingAccount ringAccount) {
try {
ringAccount.getDeviceRegistry().removeRingDevice(id);
} catch (DeviceNotFoundException ignored) {
logger.warn("Tried to remove a device that was not present in the ring account: {}", id);
}
}
}
super.handleRemoval();
}
}

View File

@ -21,7 +21,9 @@ import java.net.InetAddress;
import java.net.NetworkInterface;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledFuture;
@ -34,8 +36,9 @@ import org.openhab.binding.ring.internal.RingAccount;
import org.openhab.binding.ring.internal.RingDeviceRegistry;
import org.openhab.binding.ring.internal.RingVideoServlet;
import org.openhab.binding.ring.internal.data.Profile;
import org.openhab.binding.ring.internal.data.RingDevices;
import org.openhab.binding.ring.internal.data.RingDevicesTO;
import org.openhab.binding.ring.internal.data.RingEventTO;
import org.openhab.binding.ring.internal.discovery.RingDiscoveryService;
import org.openhab.binding.ring.internal.errors.AuthenticationException;
import org.openhab.binding.ring.internal.errors.DuplicateIdException;
import org.openhab.binding.ring.internal.utils.RingUtils;
@ -50,6 +53,7 @@ import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseBridgeHandler;
import org.openhab.core.thing.binding.ThingHandlerService;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.osgi.service.http.HttpService;
@ -75,7 +79,6 @@ public class AccountHandler extends BaseBridgeHandler implements RingAccount {
private @Nullable Runnable runnableVideo = null;
private @Nullable RingVideoServlet ringVideoServlet;
private final HttpService httpService;
private final String thingId;
// Current status
protected OnOffType status = OnOffType.OFF;
@ -92,7 +95,7 @@ public class AccountHandler extends BaseBridgeHandler implements RingAccount {
/**
* The registry.
*/
private final RingDeviceRegistry registry = RingDeviceRegistry.getInstance();
private final RingDeviceRegistry registry;
/**
* The RestClient is used to connect to the Ring Account.
*/
@ -129,7 +132,7 @@ public class AccountHandler extends BaseBridgeHandler implements RingAccount {
this.networkAddressService = networkAddressService;
this.httpService = httpService;
this.videoExecutorService = Executors.newCachedThreadPool();
this.thingId = this.getThing().getUID().getId();
this.registry = new RingDeviceRegistry();
}
@Override
@ -227,7 +230,7 @@ public class AccountHandler extends BaseBridgeHandler implements RingAccount {
private void saveRefreshTokenToFile(String refreshToken) {
String folderName = OpenHAB.getUserDataFolder() + "/ring";
String thingId = this.thingId;
String thingId = getThing().getUID().getId();
File folder = new File(folderName);
String fileName = folderName + "/ring." + thingId + ".refreshToken";
@ -246,7 +249,7 @@ public class AccountHandler extends BaseBridgeHandler implements RingAccount {
private String getRefreshTokenFromFile() {
String refreshToken = "";
String folderName = OpenHAB.getUserDataFolder() + "/ring";
String thingId = this.thingId;
String thingId = getThing().getUID().getId();
String fileName = folderName + "/ring." + thingId + ".refreshToken";
File file = new File(fileName);
if (!file.exists()) {
@ -398,8 +401,8 @@ public class AccountHandler extends BaseBridgeHandler implements RingAccount {
private void refreshRegistry() throws JsonParseException, AuthenticationException, DuplicateIdException {
logger.debug("AccountHandler - refreshRegistry");
RingDevices ringDevices = restClient.getRingDevices(userProfile, this);
registry.addRingDevices(ringDevices.getRingDevices());
RingDevicesTO ringDevices = restClient.getRingDevices(userProfile, this);
registry.addOrUpdateRingDevices(ringDevices);
}
protected void minuteTick() {
@ -578,21 +581,6 @@ public class AccountHandler extends BaseBridgeHandler implements RingAccount {
return "";
}
@Override
public @Nullable RestClient getRestClient() {
return restClient;
}
@Override
public @Nullable Profile getProfile() {
return userProfile;
}
@Override
public String getThingId() {
return thingId;
}
/**
* Dispose of the refreshJob nicely.
*/
@ -607,4 +595,14 @@ public class AccountHandler extends BaseBridgeHandler implements RingAccount {
this.videoExecutorService = null;
super.dispose();
}
@Override
public RingDeviceRegistry getDeviceRegistry() {
return registry;
}
@Override
public Collection<Class<? extends ThingHandlerService>> getServices() {
return Set.of(RingDiscoveryService.class);
}
}

View File

@ -47,9 +47,9 @@ public class ChimeHandler extends RingDeviceHandler {
logger.debug("Initializing Chime handler");
super.initialize();
RingDeviceRegistry registry = RingDeviceRegistry.getInstance();
RingDeviceRegistry registry = getDeviceRegistry();
String id = getThing().getUID().getId();
if (registry.isInitialized()) {
if (registry != null && registry.isInitialized()) {
try {
linkDevice(id, Chime.class);
updateStatus(ThingStatus.ONLINE);

View File

@ -54,9 +54,9 @@ public class DoorbellHandler extends RingDeviceHandler {
logger.debug("Initializing Doorbell handler");
super.initialize();
RingDeviceRegistry registry = RingDeviceRegistry.getInstance();
RingDeviceRegistry registry = getDeviceRegistry();
String id = getThing().getUID().getId();
if (registry.isInitialized()) {
if (registry != null && registry.isInitialized()) {
try {
linkDevice(id, Doorbell.class);
updateStatus(ThingStatus.ONLINE);
@ -102,13 +102,13 @@ public class DoorbellHandler extends RingDeviceHandler {
if (device == null) {
initialize();
}
RingDeviceTO deviceTO = gson.fromJson(device.getJsonObject(), RingDeviceTO.class);
if ((deviceTO != null) && (deviceTO.health.batteryPercentage != lastBattery)) {
RingDeviceTO deviceTO = device.getDeviceStatus();
if (deviceTO.health.batteryPercentage != lastBattery) {
logger.debug("Battery Level: {}", deviceTO.health.batteryPercentage);
ChannelUID channelUID = new ChannelUID(thing.getUID(), CHANNEL_STATUS_BATTERY);
updateState(channelUID, new DecimalType(deviceTO.health.batteryPercentage));
lastBattery = deviceTO.health.batteryPercentage;
} else if (deviceTO != null) {
} else {
logger.debug("Battery Level Unchanged for {} - {} vs {}", getThing().getUID().getId(),
deviceTO.health.batteryPercentage, lastBattery);
}

View File

@ -53,9 +53,9 @@ public class OtherDeviceHandler extends RingDeviceHandler {
logger.debug("Initializing Other Device handler");
super.initialize();
RingDeviceRegistry registry = RingDeviceRegistry.getInstance();
RingDeviceRegistry registry = getDeviceRegistry();
String id = getThing().getUID().getId();
if (registry.isInitialized()) {
if (registry != null && registry.isInitialized()) {
try {
linkDevice(id, OtherDevice.class);
updateStatus(ThingStatus.ONLINE);
@ -102,13 +102,13 @@ public class OtherDeviceHandler extends RingDeviceHandler {
initialize();
}
RingDeviceTO deviceTO = gson.fromJson(device.getJsonObject(), RingDeviceTO.class);
if ((deviceTO != null) && (deviceTO.health.batteryPercentage != lastBattery)) {
RingDeviceTO deviceTO = device.getDeviceStatus();
if (deviceTO.health.batteryPercentage != lastBattery) {
logger.debug("Battery Level: {}", deviceTO.health.batteryPercentage);
ChannelUID channelUID = new ChannelUID(thing.getUID(), CHANNEL_STATUS_BATTERY);
updateState(channelUID, new DecimalType(deviceTO.health.batteryPercentage));
lastBattery = deviceTO.health.batteryPercentage;
} else if (deviceTO != null) {
} else {
logger.debug("Battery Level Unchanged for {} - {} vs {}", getThing().getUID().getId(),
deviceTO.health.batteryPercentage, lastBattery);

View File

@ -16,6 +16,7 @@ import static org.openhab.binding.ring.RingBindingConstants.*;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.ring.internal.RingAccount;
import org.openhab.binding.ring.internal.RingDeviceRegistry;
import org.openhab.binding.ring.internal.data.RingDevice;
import org.openhab.binding.ring.internal.data.RingDeviceTO;
@ -26,8 +27,10 @@ import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.IncreaseDecreaseType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.UpDownType;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.binding.BridgeHandler;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
@ -53,6 +56,17 @@ public abstract class RingDeviceHandler extends AbstractRingHandler {
super(thing, gson);
}
protected @Nullable RingDeviceRegistry getDeviceRegistry() {
Bridge bridge = getBridge();
if (bridge != null) {
BridgeHandler bridgeHandler = bridge.getHandler();
if (bridgeHandler instanceof RingAccount ringAccount) {
return ringAccount.getDeviceRegistry();
}
}
return null;
}
/**
* Link the device, and update the device with the status CONFIGURED.
*
@ -63,17 +77,15 @@ public abstract class RingDeviceHandler extends AbstractRingHandler {
*/
protected void linkDevice(String id, Class<?> deviceClass)
throws DeviceNotFoundException, IllegalDeviceClassException {
device = RingDeviceRegistry.getInstance().getRingDevice(id);
if (device != null) {
RingDeviceTO deviceTO = gson.fromJson(device.getJsonObject(), RingDeviceTO.class);
RingDeviceRegistry registry = getDeviceRegistry();
if (registry != null) {
device = registry.getRingDevice(id);
RingDeviceTO deviceTO = device.getDeviceStatus();
if (deviceClass.equals(device.getClass())) {
device.setRegistrationStatus(RingDeviceRegistry.Status.CONFIGURED);
device.setRingDeviceHandler(this);
if (deviceTO != null) {
thing.setProperty("Description", deviceTO.description);
thing.setProperty("Kind", deviceTO.kind);
thing.setProperty("Device ID", deviceTO.deviceId);
}
thing.setProperty("Description", deviceTO.description);
thing.setProperty("Kind", deviceTO.kind);
thing.setProperty("Device ID", deviceTO.deviceId);
} else {
throw new IllegalDeviceClassException("Class '" + deviceClass.getName() + "' expected but '"
+ device.getClass().getName() + "' found.");
@ -93,10 +105,8 @@ public abstract class RingDeviceHandler extends AbstractRingHandler {
updateState(channelUID, enabled);
break;
case CHANNEL_STATUS_BATTERY:
RingDeviceTO deviceTO = gson.fromJson(device.getJsonObject(), RingDeviceTO.class);
if (deviceTO != null) {
updateState(channelUID, new DecimalType(deviceTO.health.batteryPercentage));
}
RingDeviceTO deviceTO = device.getDeviceStatus();
updateState(channelUID, new DecimalType(deviceTO.health.batteryPercentage));
break;
default:
logger.debug("Command received for an unknown channel: {}", channelUID.getId());

View File

@ -54,9 +54,9 @@ public class StickupcamHandler extends RingDeviceHandler {
logger.debug("Initializing Stickupcam handler");
super.initialize();
RingDeviceRegistry registry = RingDeviceRegistry.getInstance();
RingDeviceRegistry registry = getDeviceRegistry();
String id = getThing().getUID().getId();
if (registry.isInitialized()) {
if (registry != null && registry.isInitialized()) {
try {
linkDevice(id, Stickupcam.class);
updateStatus(ThingStatus.ONLINE);
@ -102,13 +102,13 @@ public class StickupcamHandler extends RingDeviceHandler {
if (device == null) {
initialize();
}
RingDeviceTO deviceTO = gson.fromJson(device.getJsonObject(), RingDeviceTO.class);
if ((deviceTO != null) && (deviceTO.health.batteryPercentage != lastBattery)) {
RingDeviceTO deviceTO = device.getDeviceStatus();
if (deviceTO.health.batteryPercentage != lastBattery) {
logger.debug("Battery Level: {}", deviceTO.battery);
ChannelUID channelUID = new ChannelUID(thing.getUID(), CHANNEL_STATUS_BATTERY);
updateState(channelUID, new DecimalType(deviceTO.health.batteryPercentage));
lastBattery = deviceTO.health.batteryPercentage;
} else if (deviceTO != null) {
} else {
logger.debug("Battery Level Unchanged for {} - {} vs {}", getThing().getUID().getId(),
deviceTO.health.batteryPercentage, lastBattery);

View File

@ -46,10 +46,9 @@ import javax.net.ssl.TrustManager;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.ring.internal.data.DataFactory;
import org.openhab.binding.ring.internal.data.ParamBuilder;
import org.openhab.binding.ring.internal.data.Profile;
import org.openhab.binding.ring.internal.data.RingDevices;
import org.openhab.binding.ring.internal.data.RingDevicesTO;
import org.openhab.binding.ring.internal.data.RingEventTO;
import org.openhab.binding.ring.internal.errors.AuthenticationException;
import org.openhab.binding.ring.internal.utils.RingUtils;
@ -277,12 +276,7 @@ public class RestClient {
}
JsonObject oauthToken = getOauthToken(username, password, refToken);
String jsonResult = postRequest(ApiConstants.URL_SESSION, DataFactory.getSessionParams(hardwareId),
oauthToken.get("access_token").getAsString());
JsonObject obj = JsonParser.parseString(jsonResult).getAsJsonObject();
return new Profile((JsonObject) obj.get("profile"), oauthToken.get("refresh_token").getAsString(),
oauthToken.get("access_token").getAsString());
return new Profile(oauthToken.get("refresh_token").getAsString(), oauthToken.get("access_token").getAsString());
}
/**
@ -522,12 +516,11 @@ public class RestClient {
* @throws AuthenticationException when request is invalid.
* @throws JsonParseException when response is invalid JSON.
*/
public RingDevices getRingDevices(Profile profile, RingAccount ringAccount)
public RingDevicesTO getRingDevices(Profile profile, RingAccount ringAccount)
throws JsonParseException, AuthenticationException {
logger.debug("RestClient - getRingDevices");
String jsonResult = getRequest(ApiConstants.URL_DEVICES, profile);
JsonObject obj = JsonParser.parseString(jsonResult).getAsJsonObject();
return new RingDevices(obj, ringAccount);
return Objects.requireNonNull(gson.fromJson(jsonResult, RingDevicesTO.class));
}
/**

View File

@ -13,8 +13,6 @@
package org.openhab.binding.ring.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.ring.internal.data.Profile;
/**
* The AccountHandler implements this interface to facilitate the
@ -25,26 +23,10 @@ import org.openhab.binding.ring.internal.data.Profile;
*/
@NonNullByDefault
public interface RingAccount {
/**
* Get the linked REST client.
* Get the Device Registry
*
* @return the REST client.
* @return the ring device registry
*/
public @Nullable RestClient getRestClient();
/**
* Get the linked user profile.
*
* @return the user profile.
*/
public @Nullable Profile getProfile();
/**
* Get the Account Handler Thing ID
* *
*
* @return the ring account thing id.
*/
public String getThingId();
RingDeviceRegistry getDeviceRegistry();
}

View File

@ -13,19 +13,22 @@
package org.openhab.binding.ring.internal;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.ring.internal.data.Chime;
import org.openhab.binding.ring.internal.data.Doorbell;
import org.openhab.binding.ring.internal.data.OtherDevice;
import org.openhab.binding.ring.internal.data.RingDevice;
import org.openhab.binding.ring.internal.data.RingDeviceTO;
import org.openhab.binding.ring.internal.data.RingDevicesTO;
import org.openhab.binding.ring.internal.data.Stickupcam;
import org.openhab.binding.ring.internal.errors.DeviceNotFoundException;
import org.openhab.binding.ring.internal.errors.DuplicateIdException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
/**
* Singleton registry of found devices.
*
@ -35,15 +38,6 @@ import com.google.gson.Gson;
@NonNullByDefault
public class RingDeviceRegistry {
private final Gson gson = new Gson();
/**
* static Singleton instance.
*/
private static final RingDeviceRegistry INSTANCE = new RingDeviceRegistry();
/**
* The logger.
*/
private final Logger logger = LoggerFactory.getLogger(RingDeviceRegistry.class);
/**
* Will be set after initialization.
@ -51,51 +45,35 @@ public class RingDeviceRegistry {
private boolean initialized;
/**
* Key: device id.
* Value: the RingDevice implementation object.
* Key: device id. Value: the RingDevice implementation object.
*/
private ConcurrentHashMap<String, RingDevice> devices = new ConcurrentHashMap<>();
private final Map<String, RingDevice> devices = new ConcurrentHashMap<>();
/**
* Return a singleton instance of RingDeviceRegistry.
*/
public static RingDeviceRegistry getInstance() {
return INSTANCE;
}
/**
* Add a new ring device.
*/
public void addRingDevice(RingDevice ringDevice) throws DuplicateIdException {
RingDeviceTO deviceTO = gson.fromJson(ringDevice.getJsonObject(), RingDeviceTO.class);
if (deviceTO != null) {
if (devices.containsKey(deviceTO.id)) {
throw new DuplicateIdException("Ring device with duplicate id " + deviceTO.id + " ignored");
private void addOrUpdateRingDevice(RingDeviceTO deviceTO, Function<RingDeviceTO, RingDevice> creator) {
devices.compute(deviceTO.id, (id, existing) -> {
if (existing == null) {
RingDevice device = creator.apply(deviceTO);
device.setRegistrationStatus(Status.ADDED);
return device;
} else {
ringDevice.setRegistrationStatus(Status.ADDED);
devices.put(deviceTO.id, ringDevice);
logger.debug(
"RingDeviceRegistry - addRingDevices - Ring device with duplicate id {} ignored. Updating Json.",
deviceTO.id);
existing.setDeviceStatus(deviceTO);
return existing;
}
}
});
}
/**
* Add a new ring device collection.
*/
public synchronized void addRingDevices(Collection<RingDevice> ringDevices) {
for (RingDevice device : ringDevices) {
RingDeviceTO deviceTO = gson.fromJson(device.getJsonObject(), RingDeviceTO.class);
if (deviceTO != null) {
logger.debug("RingDeviceRegistry - addRingDevices - Trying: {}", deviceTO.id);
try {
addRingDevice(device);
} catch (DuplicateIdException e) {
logger.debug(
"RingDeviceRegistry - addRingDevices - Ring device with duplicate id {} ignored. Updating Json.",
deviceTO.id);
devices.get(deviceTO.id).setJsonObject(device.getJsonObject());
}
}
}
public synchronized void addOrUpdateRingDevices(RingDevicesTO ringDevices) {
ringDevices.doorbells.forEach(deviceTO -> addOrUpdateRingDevice(deviceTO, Doorbell::new));
ringDevices.chimes.forEach(deviceTO -> addOrUpdateRingDevice(deviceTO, Chime::new));
ringDevices.stickupCams.forEach(deviceTO -> addOrUpdateRingDevice(deviceTO, Stickupcam::new));
ringDevices.other.forEach(deviceTO -> addOrUpdateRingDevice(deviceTO, OtherDevice::new));
initialized = true;
}
@ -115,9 +93,10 @@ public class RingDeviceRegistry {
* @return the RingDevice instance from the registry.
* @throws DeviceNotFoundException
*/
public @Nullable RingDevice getRingDevice(String id) throws DeviceNotFoundException {
if (devices.containsKey(id)) {
return devices.get(id);
public RingDevice getRingDevice(String id) throws DeviceNotFoundException {
RingDevice device = devices.get(id);
if (device != null) {
return device;
} else {
throw new DeviceNotFoundException("Device with id '" + id + "' not found");
}
@ -130,8 +109,9 @@ public class RingDeviceRegistry {
* @throws DeviceNotFoundException
*/
public void removeRingDevice(String id) throws DeviceNotFoundException {
if (devices.containsKey(id)) {
devices.remove(id);
RingDevice device = devices.get(id);
if (device != null) {
device.setRegistrationStatus(Status.ADDED);
} else {
throw new DeviceNotFoundException("Device with id '" + id + "' not found");
}
@ -164,7 +144,6 @@ public class RingDeviceRegistry {
* The registry status of the device.
*
* @author Wim Vissers
*
*/
public enum Status {
/**
@ -172,8 +151,7 @@ public class RingDeviceRegistry {
*/
ADDED,
/**
* When reported to the system as discovered device. It will show up
* in the inbox.
* When reported to the system as discovered device. It will show up in the inbox.
*/
DISCOVERED,
/**

View File

@ -13,16 +13,10 @@
package org.openhab.binding.ring.internal.data;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.ring.handler.RingDeviceHandler;
import org.openhab.binding.ring.internal.RingAccount;
import org.openhab.binding.ring.internal.RingDeviceRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
/**
* Interface common to all Ring devices.
*
@ -32,31 +26,12 @@ import com.google.gson.JsonObject;
@NonNullByDefault
public abstract class AbstractRingDevice implements RingDevice {
private final Logger logger = LoggerFactory.getLogger(AbstractRingDevice.class);
public final Gson gson = new Gson();
/**
* The JsonObject contains the data retrieved from the Ring API,
* or the data to send to the API.
*/
protected JsonObject jsonObject = new JsonObject();
/**
* The registration status.
*/
private RingDeviceTO deviceStatus;
private RingDeviceRegistry.Status registrationStatus = RingDeviceRegistry.Status.ADDED;
/**
* The linked Ring account.
*/
private final RingAccount ringAccount;
/**
* The linked RingDeviceHandler.
*/
private @Nullable RingDeviceHandler ringDeviceHandler;
protected AbstractRingDevice(JsonObject jsonObject, RingAccount ringAccount) {
this.jsonObject = jsonObject;
this.ringAccount = ringAccount;
protected AbstractRingDevice(RingDeviceTO jsonObject) {
this.deviceStatus = jsonObject;
}
/**
@ -72,52 +47,21 @@ public abstract class AbstractRingDevice implements RingDevice {
/**
* Set the registration status.
*
* @param status
* @param registrationStatus
*/
@Override
public void setRegistrationStatus(RingDeviceRegistry.Status registrationStatus) {
this.registrationStatus = registrationStatus;
}
/**
* Get the linked Ring Device Handler.
*
* @return the handler.
*/
@Override
@Nullable
public RingDeviceHandler getRingDeviceHandler() {
return ringDeviceHandler;
}
/**
* Set the linked Ring Device Handler.
*
* @param ringDeviceHandler the handler.
*/
@Override
public void setRingDeviceHandler(RingDeviceHandler ringDeviceHandler) {
this.ringDeviceHandler = ringDeviceHandler;
}
/**
* Get the linked Ring account.
*
* @return the account.
*/
@Override
public RingAccount getRingAccount() {
return ringAccount;
public void setDeviceStatus(RingDeviceTO ringDeviceTO) {
this.deviceStatus = ringDeviceTO;
logger.trace("AbstractRingDevice - setJsonObject - Updated JSON: {}", ringDeviceTO);
}
@Override
public void setJsonObject(JsonObject jsonObject) {
this.jsonObject = jsonObject;
logger.trace("AbstractRingDevice - setJsonObject - Updated JSON: {}", this.jsonObject);
}
@Override
public JsonObject getJsonObject() {
return this.jsonObject;
public RingDeviceTO getDeviceStatus() {
return deviceStatus;
}
}

View File

@ -13,12 +13,6 @@
package org.openhab.binding.ring.internal.data;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.ring.internal.RingAccount;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.thing.ThingUID;
import com.google.gson.JsonObject;
/**
* @author Ben Rosenblum - Initial contribution
@ -29,24 +23,9 @@ public class Chime extends AbstractRingDevice {
/**
* Create Chime instance from JSON object.
*
* @param jsonChime the JSON Chime retrieved from the Ring API.
* @param ringAccount the Ring Account in use
* @param deviceTO the JSON Chime retrieved from the Ring API.
*/
public Chime(JsonObject jsonChime, RingAccount ringAccount) {
super(jsonChime, ringAccount);
}
/**
* Get the DiscoveryResult object to identify the device as
* discovered thing.
*
* @return the device as DiscoveryResult instance.
*/
@Override
public DiscoveryResult getDiscoveryResult(RingDeviceTO deviceTO) {
DiscoveryResult result = DiscoveryResultBuilder
.create(new ThingUID("ring:chime:" + getRingAccount().getThingId() + ":" + deviceTO.id))
.withLabel("Ring Chime - " + deviceTO.description).build();
return result;
public Chime(RingDeviceTO deviceTO) {
super(deviceTO);
}
}

View File

@ -1,51 +0,0 @@
/*
* Copyright (c) 2010-2025 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.data;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.ring.internal.ApiConstants;
/**
* @author Wim Vissers - Initial contribution
* @author Ben Rosenblum - Updated for OH4 / New Maintainer
*/
@NonNullByDefault
public class DataFactory {
public static String getOauthData(String username, String password) {
return "";
}
/**
* Get GET parameters for the session API resource.
*
* @return
*/
public static String getSessionParams(String hardwareId) {
ParamBuilder pb = new ParamBuilder(false);
pb.add("device[os]", "android");
pb.add("device[hardware_id]", hardwareId);
pb.add("device[app_brand]", "ring");
pb.add("device[metadata][device_model]", "VirtualBox");
pb.add("device[metadata][resolution]", "600x800");
pb.add("device[metadata][app_version]", "1.7.29");
pb.add("device[metadata][app_installation_date]", "");
pb.add("device[metadata][os_version]", "4.4.4");
pb.add("device[metadata][manufacturer]", "innotek GmbH");
pb.add("device[metadata][is_tablet]", "true");
pb.add("device[metadata][linphone_initialized]", "true");
pb.add("device[metadata][language]", "en");
pb.add("api_version", "" + ApiConstants.API_VERSION);
return pb.toString();
}
}

View File

@ -13,12 +13,6 @@
package org.openhab.binding.ring.internal.data;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.ring.internal.RingAccount;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.thing.ThingUID;
import com.google.gson.JsonObject;
/**
* @author Wim Vissers - Initial contribution
@ -27,28 +21,12 @@ import com.google.gson.JsonObject;
@NonNullByDefault
public class Doorbell extends AbstractRingDevice {
/**
* Create Doorbell instance from JSON object.
*
* @param jsonDoorbell the JSON doorbell (doorbot) retrieved from the Ring API.
* @param ringAccount the Ring Account in use
* @param deviceTO the JSON doorbell (doorbot) retrieved from the Ring API.
*/
public Doorbell(JsonObject jsonDoorbell, RingAccount ringAccount) {
super(jsonDoorbell, ringAccount);
}
/**
* Get the DiscoveryResult object to identify the device as
* discovered thing.
*
* @return the device as DiscoveryResult instance.
*/
@Override
public DiscoveryResult getDiscoveryResult(RingDeviceTO deviceTO) {
DiscoveryResult result = DiscoveryResultBuilder
.create(new ThingUID("ring:doorbell:" + getRingAccount().getThingId() + ":" + deviceTO.id))
.withLabel("Ring Video Doorbell - " + deviceTO.description).build();
return result;
public Doorbell(RingDeviceTO deviceTO) {
super(deviceTO);
}
}

View File

@ -1,78 +0,0 @@
/*
* Copyright (c) 2010-2025 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.data;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* @author Wim Vissers - Initial contribution
* @author Ben Rosenblum - Updated for OH4 / New Maintainer
*/
@NonNullByDefault
public enum Feature {
REMOTE_LOGGING_FORMAT_STORING,
REMOTE_LOGGING_LEVEL,
SUBSCRIPTIONS_ENABLED,
STICKUPCAM_SETUP_ENABLED,
VOD_ENABLED,
NW_ENABLED,
NW_V2_ENABLED,
NW_USER_ACTIVATED,
RINGPLUS_ENABLED,
LPD_ENABLED,
REACTIVE_SNOOZING_ENABLED,
PROACTIVE_SNOOZING_ENABLED,
OWNER_PROACTIVE_SNOOZING_ENABLED,
LIVE_VIEW_SETTINGS_ENABLED,
DELETE_ALL_SETTINGS_ENABLED,
POWER_CABLE_ENABLED,
DEVICE_HEALTH_ALERTS_ENABLED,
CHIME_PRO_ENABLED,
MULTIPLE_CALLS_ENABLED,
UJET_ENABLED,
MULTIPLE_DELETE_ENABLED,
DELETE_ALL_ENABLED,
LPD_MOTION_ANNOUNCEMENT_ENABLED,
STARRED_EVENTS_ENABLED,
CHIME_DND_ENABLED,
VIDEO_SEARCH_ENABLED,
FLOODLIGHT_CAM_ENABLED,
NW_LARGER_AREA_ENABLED,
RING_CAM_BATTERY_ENABLED,
ELITE_CAM_ENABLED,
DOORBELL_V2_ENABLED,
SPOTLIGHT_BATTERY_DASHBOARD_CONTROLS_ENABLED,
BYPASS_ACCOUNT_VERIFICATION,
LEGACY_CVR_RETENTION_ENABLED,
NEW_DASHBOARD_ENABLED,
RING_CAM_ENABLED,
RING_SEARCH_ENABLED,
RING_CAM_MOUNT_ENABLED,
RING_ALARM_ENABLED,
IN_APP_CALL_NOTIFICATIONS,
RING_CASH_ELIGIBLE_ENABLED,
NEW_RING_PLAYER_ENABLED,
APP_ALERT_TONES_ENABLED,
MOTION_SNOOZING_ENABLED;
/**
* The enum is named according to the json names retrieved from
* the Ring API, but in upper case.
*
* @return the json name.
*/
public String getJsonName() {
return this.toString().toLowerCase();
}
}

View File

@ -13,12 +13,6 @@
package org.openhab.binding.ring.internal.data;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.ring.internal.RingAccount;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.thing.ThingUID;
import com.google.gson.JsonObject;
/**
* @author Ben Rosenblum - Initial contribution
@ -29,24 +23,9 @@ public class OtherDevice extends AbstractRingDevice {
/**
* Create OtherDevice instance from JSON object.
*
* @param jsonOtherDevice the JSON Other retrieved from the Ring API.
* @param ringAccount the Ring Account in use
* @param deviceTO the JSON Other retrieved from the Ring API.
*/
public OtherDevice(JsonObject jsonOtherDevice, RingAccount ringAccount) {
super(jsonOtherDevice, ringAccount);
}
/**
* Get the DiscoveryResult object to identify the device as
* discovered thing.
*
* @return the device as DiscoveryResult instance.
*/
@Override
public DiscoveryResult getDiscoveryResult(RingDeviceTO deviceTO) {
DiscoveryResult result = DiscoveryResultBuilder
.create(new ThingUID("ring:otherdevice:" + getRingAccount().getThingId() + ":" + deviceTO.id))
.withLabel("Ring Other Device - " + deviceTO.description).build();
return result;
public OtherDevice(RingDeviceTO deviceTO) {
super(deviceTO);
}
}

View File

@ -14,8 +14,6 @@ package org.openhab.binding.ring.internal.data;
import org.eclipse.jdt.annotation.NonNullByDefault;
import com.google.gson.JsonObject;
/**
* {"profile":{
* "id":4445516,
@ -83,23 +81,18 @@ import com.google.gson.JsonObject;
@NonNullByDefault
public class Profile {
private JsonObject jsonProfile = new JsonObject();
private JsonObject jsonFeatures = new JsonObject();
private String refreshToken = "";
private String accessToken = "";
/**
* Create Profile instance from JSON String.
*
* @param jsonProfile the JSON profile retrieved from the Ring API.
* @param refreshToken needed for the refresh token so we aren't logging in every time.
* Needed as a separate parameter because it's not part of the jsonProfile object.
* @param accessToken needed for the access token so we aren't logging in every time.
* Needed as a separate parameter because it's not part of the jsonProfile object.
*/
public Profile(JsonObject jsonProfile, String refreshToken, String accessToken) {
this.jsonProfile = jsonProfile;
this.jsonFeatures = (JsonObject) jsonProfile.get("features");
public Profile(String refreshToken, String accessToken) {
this.refreshToken = refreshToken;
this.accessToken = accessToken;
}

View File

@ -13,13 +13,7 @@
package org.openhab.binding.ring.internal.data;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.ring.handler.RingDeviceHandler;
import org.openhab.binding.ring.internal.RingAccount;
import org.openhab.binding.ring.internal.RingDeviceRegistry;
import org.openhab.core.config.discovery.DiscoveryResult;
import com.google.gson.JsonObject;
/**
* Interface common to all Ring devices.
@ -29,15 +23,6 @@ import com.google.gson.JsonObject;
*/
@NonNullByDefault
public interface RingDevice {
/**
* Get the DiscoveryResult object to identify the device as
* discovered thing.
*
* @return the device as DiscoveryResult instance.
*/
DiscoveryResult getDiscoveryResult(RingDeviceTO deviceTO);
/**
* Get the registration status.
*
@ -52,29 +37,7 @@ public interface RingDevice {
*/
void setRegistrationStatus(RingDeviceRegistry.Status registrationStatus);
/**
* Get the linked Ring account.
*
* @return the account.
*/
RingAccount getRingAccount();
void setDeviceStatus(RingDeviceTO ringDeviceTO);
/**
* Get the linked Ring Device Handler.
*
* @return the handler.
*/
@Nullable
RingDeviceHandler getRingDeviceHandler();
/**
* Set the linked Ring Device Handler.
*
* @param ringDeviceHandler the handler.
*/
void setRingDeviceHandler(RingDeviceHandler ringDeviceHandler);
void setJsonObject(JsonObject jsonObject);
JsonObject getJsonObject();
RingDeviceTO getDeviceStatus();
}

View File

@ -1,148 +0,0 @@
/*
* Copyright (c) 2010-2025 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.data;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.ring.internal.ApiConstants;
import org.openhab.binding.ring.internal.RingAccount;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
/**
*
* @author Wim Vissers - Initial contribution
* @author Chris Milbert - stickupcam contribution
* @author Ben Rosenblum - Updated for OH4 / New Maintainer
*/
@NonNullByDefault
public class RingDevices {
private List<Doorbell> doorbells = new ArrayList<>();
private List<Stickupcam> stickupcams = new ArrayList<>();
private List<Chime> chimes = new ArrayList<>();
private List<OtherDevice> otherdevices = new ArrayList<>();
public RingDevices(JsonObject jsonRingDevices, RingAccount ringAccount) {
addDoorbells((JsonArray) jsonRingDevices.get(ApiConstants.DEVICES_DOORBOTS), ringAccount);
addStickupCams((JsonArray) jsonRingDevices.get(ApiConstants.DEVICES_STICKUP_CAMS), ringAccount);
addChimes((JsonArray) jsonRingDevices.get(ApiConstants.DEVICES_CHIMES), ringAccount);
addOtherDevices((JsonArray) jsonRingDevices.get(ApiConstants.DEVICES_OTHERDEVICE), ringAccount);
}
/**
* Helper method to create the doorbell list.
*
* @param jsonDoorbells
* @param ringAccount
*/
private void addDoorbells(JsonArray jsonDoorbells, RingAccount ringAccount) {
for (Object obj : jsonDoorbells) {
Doorbell doorbell = new Doorbell((JsonObject) obj, ringAccount);
doorbells.add(doorbell);
}
}
/**
* Retrieve the Doorbells Collection.
*
* @return
*/
public Collection<Doorbell> getDoorbells() {
return doorbells;
}
/**
* Helper method to create the stickupcam list.
*
* @param jsonStickupcams
* @param ringAccount
*/
private void addStickupCams(JsonArray jsonStickupcams, RingAccount ringAccount) {
for (Object obj : jsonStickupcams) {
Stickupcam stickupcam = new Stickupcam((JsonObject) obj, ringAccount);
stickupcams.add(stickupcam);
}
}
/**
* Retrieve the Stickupcams Collection.
*
* @return
*/
public Collection<Stickupcam> getStickupcams() {
return stickupcams;
}
/**
* Helper method to create the chime list.
*
* @param jsonChimes
* @param ringAccount
*/
private void addChimes(JsonArray jsonChimes, RingAccount ringAccount) {
for (Object obj : jsonChimes) {
Chime chime = new Chime((JsonObject) obj, ringAccount);
chimes.add(chime);
}
}
/**
* Retrieve the Chimes Collection.
*
* @return
*/
public Collection<Chime> getChimes() {
return chimes;
}
/**
* Helper method to create the other list.
*
* @param jsonOther
* @param ringAccount
*/
private void addOtherDevices(JsonArray jsonOtherDevices, RingAccount ringAccount) {
for (Object obj : jsonOtherDevices) {
OtherDevice otherdevice = new OtherDevice((JsonObject) obj, ringAccount);
otherdevices.add(otherdevice);
}
}
/**
* Retrieve the Others Collection.
*
* @return
*/
public Collection<OtherDevice> getOtherDevices() {
return otherdevices;
}
/**
* Retrieve a collection of all devices.
*
* @return
*/
public Collection<RingDevice> getRingDevices() {
List<RingDevice> result = new ArrayList<>();
result.addAll(doorbells);
result.addAll(stickupcams);
result.addAll(chimes);
result.addAll(otherdevices);
return result;
}
}

View File

@ -0,0 +1,34 @@
/*
* Copyright (c) 2010-2025 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.data;
import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
import com.google.gson.annotations.SerializedName;
/**
* The {@link RingDevicesTO} class is a TO containing all devices for an account
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class RingDevicesTO {
@SerializedName("doorbots")
public List<RingDeviceTO> doorbells = List.of();
public List<RingDeviceTO> chimes = List.of();
@SerializedName("stickup_cams")
public List<RingDeviceTO> stickupCams = List.of();
public List<RingDeviceTO> other = List.of();
}

View File

@ -13,12 +13,6 @@
package org.openhab.binding.ring.internal.data;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.ring.internal.RingAccount;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.thing.ThingUID;
import com.google.gson.JsonObject;
/**
* @author Chris Milbert - Initial contribution
@ -31,24 +25,9 @@ public class Stickupcam extends AbstractRingDevice {
/**
* Create Stickup Cam instance from JSON object.
*
* @param jsonStickupcam the JSON Stickup Cam retrieved from the Ring API.
* @param ringAccount the Ring Account in use
* @param deviceTO the JSON Stickup Cam retrieved from the Ring API.
*/
public Stickupcam(JsonObject jsonStickupcam, RingAccount ringAccount) {
super(jsonStickupcam, ringAccount);
}
/**
* Get the DiscoveryResult object to identify the device as
* discovered thing.
*
* @return the device as DiscoveryResult instance.
*/
@Override
public DiscoveryResult getDiscoveryResult(RingDeviceTO deviceTO) {
DiscoveryResult result = DiscoveryResultBuilder
.create(new ThingUID("ring:stickupcam:" + getRingAccount().getThingId() + ":" + deviceTO.id))
.withLabel("Ring Video Stickup Cam - " + deviceTO.description).build();
return result;
public Stickupcam(RingDeviceTO deviceTO) {
super(deviceTO);
}
}

View File

@ -14,21 +14,24 @@ package org.openhab.binding.ring.internal.discovery;
import static org.openhab.binding.ring.RingBindingConstants.*;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.time.Instant;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.ring.handler.AccountHandler;
import org.openhab.binding.ring.internal.RingDeviceRegistry;
import org.openhab.binding.ring.internal.data.Chime;
import org.openhab.binding.ring.internal.data.Doorbell;
import org.openhab.binding.ring.internal.data.RingDevice;
import org.openhab.binding.ring.internal.data.RingDeviceTO;
import org.openhab.core.config.discovery.AbstractDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryService;
import org.openhab.binding.ring.internal.data.Stickupcam;
import org.openhab.core.config.discovery.AbstractThingHandlerDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.ThingHandler;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import org.osgi.service.component.annotations.ServiceScope;
/**
* The RingDiscoveryService is responsible for auto detecting a Ring
@ -37,68 +40,39 @@ import com.google.gson.Gson;
* @author Wim Vissers - Initial contribution
* @author Chris Milbert - Stickupcam contribution
* @author Ben Rosenblum - Updated for OH4 / New Maintainer
* @author Jan N. Klug - Refactored to ThingHandlerService
*/
@Component(service = DiscoveryService.class, immediate = true, configurationPid = "discovery.ring")
@Component(scope = ServiceScope.PROTOTYPE, service = RingDiscoveryService.class)
@NonNullByDefault
public class RingDiscoveryService extends AbstractDiscoveryService {
private Logger logger = LoggerFactory.getLogger(RingDiscoveryService.class);
private @Nullable ScheduledFuture<?> discoveryJob;
private final Gson gson = new Gson();
public class RingDiscoveryService extends AbstractThingHandlerDiscoveryService<AccountHandler> {
public RingDiscoveryService() {
super(SUPPORTED_THING_TYPES_UIDS, 5, true);
}
public void activate() {
logger.debug("Starting Ring discovery...");
startScan();
startBackgroundDiscovery();
}
@Override
public void deactivate() {
logger.debug("Stopping Ring discovery...");
stopBackgroundDiscovery();
stopScan();
}
private void discover() {
RingDeviceRegistry registry = RingDeviceRegistry.getInstance();
for (RingDevice device : registry.getRingDevices(RingDeviceRegistry.Status.ADDED)) {
RingDeviceTO deviceTO = gson.fromJson(device.getJsonObject(), RingDeviceTO.class);
if (deviceTO != null) {
thingDiscovered(device.getDiscoveryResult(deviceTO));
registry.setStatus(deviceTO.id, RingDeviceRegistry.Status.DISCOVERED);
}
}
}
private void refresh() {
discover();
}
@Override
protected void startBackgroundDiscovery() {
discoveryJob = scheduler.scheduleWithFixedDelay(this::refresh, 0, 120, TimeUnit.SECONDS);
}
@Override
protected void stopBackgroundDiscovery() {
logger.info("Stop Ring background discovery");
ScheduledFuture<?> job = discoveryJob;
if (job != null) {
job.cancel(true);
}
discoveryJob = null;
super(AccountHandler.class, SUPPORTED_THING_TYPES_UIDS, 5, true);
}
@Override
protected void startScan() {
logger.debug("Starting device search...");
discover();
ThingHandler thingHandler = getThingHandler();
if (thingHandler instanceof AccountHandler accountHandler) {
RingDeviceRegistry registry = accountHandler.getDeviceRegistry();
ThingUID bridgeUID = accountHandler.getThing().getUID();
for (RingDevice device : registry.getRingDevices(RingDeviceRegistry.Status.ADDED)) {
RingDeviceTO deviceTO = device.getDeviceStatus();
ThingTypeUID thingTypeUID = switch (device) {
case Chime chime -> THING_TYPE_CHIME;
case Doorbell doorbell -> THING_TYPE_DOORBELL;
case Stickupcam stickupcam -> THING_TYPE_STICKUPCAM;
default -> THING_TYPE_OTHERDEVICE;
};
DiscoveryResult result = DiscoveryResultBuilder
.create(new ThingUID(thingTypeUID, bridgeUID, deviceTO.id)).withLabel(deviceTO.description)
.withBridge(bridgeUID).build();
thingDiscovered(result);
registry.setStatus(deviceTO.id, RingDeviceRegistry.Status.DISCOVERED);
}
}
}
@Override
@ -106,4 +80,10 @@ public class RingDiscoveryService extends AbstractDiscoveryService {
removeOlderResults(getTimestampOfLastScan());
super.stopScan();
}
@Override
public void dispose() {
super.dispose();
removeOlderResults(Instant.now().toEpochMilli());
}
}