[hue] Refactored discovery service to 'ThingHandlerService' (#8729)

* Refactored discovery service to ThingHandlerService
* Fixed discovery for Geofence Sensor

Signed-off-by: Christoph Weitkamp <github@christophweitkamp.de>
This commit is contained in:
Christoph Weitkamp 2020-10-16 00:51:41 +02:00 committed by GitHub
parent 9b7fb69e8d
commit 25826854b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 136 additions and 125 deletions

View File

@ -108,8 +108,14 @@ public class FullHueObject extends HueObject {
* *
* @return the unique id, can be null for some virtual types like the daylight sensor * @return the unique id, can be null for some virtual types like the daylight sensor
*/ */
public @Nullable String getUniqueID() { public @Nullable String getUniqueID() {
return uniqueid; return uniqueid;
} }
/**
* Sets the unique id of the object.
*/
protected void setUniqueID(final String uniqueid) {
this.uniqueid = uniqueid;
}
} }

View File

@ -16,6 +16,8 @@ import java.lang.reflect.Type;
import java.time.Duration; import java.time.Duration;
import java.util.Map; import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
/** /**
@ -26,16 +28,14 @@ import com.google.gson.reflect.TypeToken;
* @author Denis Dudnik - moved Jue library source code inside the smarthome Hue binding * @author Denis Dudnik - moved Jue library source code inside the smarthome Hue binding
* @author Samuel Leisering - added GSon Type to FullLight, refactored content to {@link FullHueObject} * @author Samuel Leisering - added GSon Type to FullLight, refactored content to {@link FullHueObject}
*/ */
@NonNullByDefault
public class FullLight extends FullHueObject { public class FullLight extends FullHueObject {
public static final Type GSON_TYPE = new TypeToken<Map<String, FullLight>>() { public static final Type GSON_TYPE = new TypeToken<Map<String, FullLight>>() {
}.getType(); }.getType();
private State state; private @NonNullByDefault({}) State state;
private final long fadetime = 400; // milliseconds private final long fadetime = 400; // milliseconds
FullLight() {
}
/** /**
* Returns the current state of the light. * Returns the current state of the light.
* *

View File

@ -15,6 +15,8 @@ package org.openhab.binding.hue.internal;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.Map; import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
/** /**
@ -23,7 +25,10 @@ import com.google.gson.reflect.TypeToken;
* @author Samuel Leisering - Initial contribution * @author Samuel Leisering - Initial contribution
* @author Christoph Weitkamp - Initial contribution * @author Christoph Weitkamp - Initial contribution
*/ */
@NonNullByDefault
public class FullSensor extends FullHueObject { public class FullSensor extends FullHueObject {
public static final Type GSON_TYPE = new TypeToken<Map<String, FullSensor>>() {
}.getType();
public static final String STATE_LAST_UPDATED = "lastupdated"; public static final String STATE_LAST_UPDATED = "lastupdated";
public static final String STATE_BUTTON_EVENT = "buttonevent"; public static final String STATE_BUTTON_EVENT = "buttonevent";
@ -46,11 +51,8 @@ public class FullSensor extends FullHueObject {
public static final String CONFIG_LIGHT_LEVEL_THRESHOLD_DARK = "tholddark"; public static final String CONFIG_LIGHT_LEVEL_THRESHOLD_DARK = "tholddark";
public static final String CONFIG_LIGHT_LEVEL_THRESHOLD_OFFSET = "tholdoffset"; public static final String CONFIG_LIGHT_LEVEL_THRESHOLD_OFFSET = "tholdoffset";
public static final Type GSON_TYPE = new TypeToken<Map<String, FullSensor>>() { private @NonNullByDefault({}) Map<String, Object> state;
}.getType(); private @NonNullByDefault({}) Map<String, Object> config;
private Map<String, Object> state;
private Map<String, Object> config;
public Map<String, Object> getState() { public Map<String, Object> getState() {
return state; return state;

View File

@ -15,16 +15,12 @@ package org.openhab.binding.hue.internal;
import static org.openhab.binding.hue.internal.HueBindingConstants.*; import static org.openhab.binding.hue.internal.HueBindingConstants.*;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.hue.internal.discovery.HueLightDiscoveryService;
import org.openhab.binding.hue.internal.handler.HueBridgeHandler; import org.openhab.binding.hue.internal.handler.HueBridgeHandler;
import org.openhab.binding.hue.internal.handler.HueGroupHandler; import org.openhab.binding.hue.internal.handler.HueGroupHandler;
import org.openhab.binding.hue.internal.handler.HueLightHandler; import org.openhab.binding.hue.internal.handler.HueLightHandler;
@ -37,7 +33,6 @@ import org.openhab.binding.hue.internal.handler.sensors.PresenceHandler;
import org.openhab.binding.hue.internal.handler.sensors.TapSwitchHandler; import org.openhab.binding.hue.internal.handler.sensors.TapSwitchHandler;
import org.openhab.binding.hue.internal.handler.sensors.TemperatureHandler; import org.openhab.binding.hue.internal.handler.sensors.TemperatureHandler;
import org.openhab.core.config.core.Configuration; import org.openhab.core.config.core.Configuration;
import org.openhab.core.config.discovery.DiscoveryService;
import org.openhab.core.thing.Bridge; import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing; import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.ThingTypeUID;
@ -45,7 +40,6 @@ import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory; import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler; import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory; import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference; import org.osgi.service.component.annotations.Reference;
@ -75,8 +69,6 @@ public class HueThingHandlerFactory extends BaseThingHandlerFactory {
private final HueStateDescriptionOptionProvider stateOptionProvider; private final HueStateDescriptionOptionProvider stateOptionProvider;
private final Map<ThingUID, @Nullable ServiceRegistration<?>> discoveryServiceRegs = new HashMap<>();
@Activate @Activate
public HueThingHandlerFactory(final @Reference HueStateDescriptionOptionProvider stateOptionProvider) { public HueThingHandlerFactory(final @Reference HueStateDescriptionOptionProvider stateOptionProvider) {
this.stateOptionProvider = stateOptionProvider; this.stateOptionProvider = stateOptionProvider;
@ -150,9 +142,7 @@ public class HueThingHandlerFactory extends BaseThingHandlerFactory {
@Override @Override
protected @Nullable ThingHandler createHandler(Thing thing) { protected @Nullable ThingHandler createHandler(Thing thing) {
if (HueBridgeHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) { if (HueBridgeHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
HueBridgeHandler handler = new HueBridgeHandler((Bridge) thing, stateOptionProvider); return new HueBridgeHandler((Bridge) thing, stateOptionProvider);
registerLightDiscoveryService(handler);
return handler;
} else if (HueLightHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) { } else if (HueLightHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
return new HueLightHandler(thing); return new HueLightHandler(thing);
} else if (DimmerSwitchHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) { } else if (DimmerSwitchHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
@ -175,27 +165,4 @@ public class HueThingHandlerFactory extends BaseThingHandlerFactory {
return null; return null;
} }
} }
private synchronized void registerLightDiscoveryService(HueBridgeHandler bridgeHandler) {
HueLightDiscoveryService discoveryService = new HueLightDiscoveryService(bridgeHandler);
discoveryService.activate();
this.discoveryServiceRegs.put(bridgeHandler.getThing().getUID(),
bundleContext.registerService(DiscoveryService.class.getName(), discoveryService, new Hashtable<>()));
}
@Override
protected synchronized void removeHandler(ThingHandler thingHandler) {
if (thingHandler instanceof HueBridgeHandler) {
ServiceRegistration<?> serviceReg = this.discoveryServiceRegs.remove(thingHandler.getThing().getUID());
if (serviceReg != null) {
// remove discovery service, if bridge handler is removed
HueLightDiscoveryService service = (HueLightDiscoveryService) bundleContext
.getService(serviceReg.getReference());
serviceReg.unregister();
if (service != null) {
service.deactivate();
}
}
}
}
} }

View File

@ -43,9 +43,12 @@ import org.openhab.binding.hue.internal.handler.sensors.TemperatureHandler;
import org.openhab.core.config.discovery.AbstractDiscoveryService; import org.openhab.core.config.discovery.AbstractDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryResult; import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder; import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.config.discovery.DiscoveryService;
import org.openhab.core.thing.Thing; import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID; import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerService;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -64,7 +67,9 @@ import org.slf4j.LoggerFactory;
* @author Laurent Garnier - Added support for groups * @author Laurent Garnier - Added support for groups
*/ */
@NonNullByDefault @NonNullByDefault
public class HueLightDiscoveryService extends AbstractDiscoveryService { public class HueDeviceDiscoveryService extends AbstractDiscoveryService
implements DiscoveryService, ThingHandlerService {
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.unmodifiableSet(Stream public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.unmodifiableSet(Stream
.of(HueLightHandler.SUPPORTED_THING_TYPES.stream(), DimmerSwitchHandler.SUPPORTED_THING_TYPES.stream(), .of(HueLightHandler.SUPPORTED_THING_TYPES.stream(), DimmerSwitchHandler.SUPPORTED_THING_TYPES.stream(),
TapSwitchHandler.SUPPORTED_THING_TYPES.stream(), PresenceHandler.SUPPORTED_THING_TYPES.stream(), TapSwitchHandler.SUPPORTED_THING_TYPES.stream(), PresenceHandler.SUPPORTED_THING_TYPES.stream(),
@ -73,12 +78,8 @@ public class HueLightDiscoveryService extends AbstractDiscoveryService {
ClipHandler.SUPPORTED_THING_TYPES.stream(), HueGroupHandler.SUPPORTED_THING_TYPES.stream()) ClipHandler.SUPPORTED_THING_TYPES.stream(), HueGroupHandler.SUPPORTED_THING_TYPES.stream())
.flatMap(i -> i).collect(Collectors.toSet())); .flatMap(i -> i).collect(Collectors.toSet()));
private final Logger logger = LoggerFactory.getLogger(HueLightDiscoveryService.class);
private static final int SEARCH_TIME = 10;
// @formatter:off // @formatter:off
private static final Map<String, @Nullable String> TYPE_TO_ZIGBEE_ID_MAP = Stream.of( private static final Map<String, @Nullable String> TYPE_TO_ZIGBEE_ID_MAP = Map.ofEntries(
new SimpleEntry<>("on_off_light", "0000"), new SimpleEntry<>("on_off_light", "0000"),
new SimpleEntry<>("on_off_plug_in_unit", "0010"), new SimpleEntry<>("on_off_plug_in_unit", "0010"),
new SimpleEntry<>("dimmable_light", "0100"), new SimpleEntry<>("dimmable_light", "0100"),
@ -91,27 +92,50 @@ public class HueLightDiscoveryService extends AbstractDiscoveryService {
new SimpleEntry<>("clipgenericstatus", "0840"), new SimpleEntry<>("clipgenericstatus", "0840"),
new SimpleEntry<>("clipgenericflag", "0850"), new SimpleEntry<>("clipgenericflag", "0850"),
new SimpleEntry<>("zllpresence", "0107"), new SimpleEntry<>("zllpresence", "0107"),
new SimpleEntry<>("geofence", "0107"), new SimpleEntry<>("geofence", "geofencesensor"),
new SimpleEntry<>("zlltemperature", "0302"), new SimpleEntry<>("zlltemperature", "0302"),
new SimpleEntry<>("zlllightlevel", "0106") new SimpleEntry<>("zlllightlevel", "0106"));
).collect(Collectors.toMap((e) -> e.getKey(), (e) -> e.getValue()));
// @formatter:on // @formatter:on
private final HueBridgeHandler hueBridgeHandler; private static final int SEARCH_TIME = 10;
public HueLightDiscoveryService(HueBridgeHandler hueBridgeHandler) { private final Logger logger = LoggerFactory.getLogger(HueDeviceDiscoveryService.class);
super(SEARCH_TIME);
this.hueBridgeHandler = hueBridgeHandler; private @Nullable HueBridgeHandler hueBridgeHandler;
private @Nullable ThingUID bridgeUID;
public HueDeviceDiscoveryService() {
super(SUPPORTED_THING_TYPES, SEARCH_TIME);
} }
@Override
public void setThingHandler(@Nullable ThingHandler handler) {
if (handler instanceof HueBridgeHandler) {
hueBridgeHandler = (HueBridgeHandler) handler;
bridgeUID = handler.getThing().getUID();
}
}
@Override
public @Nullable ThingHandler getThingHandler() {
return hueBridgeHandler;
}
@Override
public void activate() { public void activate() {
hueBridgeHandler.registerDiscoveryListener(this); final HueBridgeHandler handler = hueBridgeHandler;
if (handler != null) {
handler.registerDiscoveryListener(this);
}
} }
@Override @Override
public void deactivate() { public void deactivate() {
removeOlderResults(new Date().getTime(), hueBridgeHandler.getThing().getUID()); removeOlderResults(new Date().getTime(), bridgeUID);
hueBridgeHandler.unregisterDiscoveryListener(); final HueBridgeHandler handler = hueBridgeHandler;
if (handler != null) {
handler.unregisterDiscoveryListener();
}
} }
@Override @Override
@ -121,26 +145,32 @@ public class HueLightDiscoveryService extends AbstractDiscoveryService {
@Override @Override
public void startScan() { public void startScan() {
List<FullLight> lights = hueBridgeHandler.getFullLights(); final HueBridgeHandler handler = hueBridgeHandler;
if (handler != null) {
List<FullLight> lights = handler.getFullLights();
for (FullLight l : lights) { for (FullLight l : lights) {
addLightDiscovery(l); addLightDiscovery(l);
} }
List<FullSensor> sensors = hueBridgeHandler.getFullSensors(); List<FullSensor> sensors = handler.getFullSensors();
for (FullSensor s : sensors) { for (FullSensor s : sensors) {
addSensorDiscovery(s); addSensorDiscovery(s);
} }
List<FullGroup> groups = hueBridgeHandler.getFullGroups(); List<FullGroup> groups = handler.getFullGroups();
for (FullGroup g : groups) { for (FullGroup g : groups) {
addGroupDiscovery(g); addGroupDiscovery(g);
} }
// search for unpaired lights // search for unpaired lights
hueBridgeHandler.startSearch(); handler.startSearch();
}
} }
@Override @Override
protected synchronized void stopScan() { protected synchronized void stopScan() {
super.stopScan(); super.stopScan();
removeOlderResults(getTimestampOfLastScan(), hueBridgeHandler.getThing().getUID()); final HueBridgeHandler handler = hueBridgeHandler;
if (handler != null) {
removeOlderResults(getTimestampOfLastScan(), handler.getThing().getUID());
}
} }
public void addLightDiscovery(FullLight light) { public void addLightDiscovery(FullLight light) {
@ -150,7 +180,6 @@ public class HueLightDiscoveryService extends AbstractDiscoveryService {
String modelId = light.getNormalizedModelID(); String modelId = light.getNormalizedModelID();
if (thingUID != null && thingTypeUID != null) { if (thingUID != null && thingTypeUID != null) {
ThingUID bridgeUID = hueBridgeHandler.getThing().getUID();
Map<String, Object> properties = new HashMap<>(); Map<String, Object> properties = new HashMap<>();
properties.put(LIGHT_ID, light.getId()); properties.put(LIGHT_ID, light.getId());
if (modelId != null) { if (modelId != null) {
@ -181,15 +210,16 @@ public class HueLightDiscoveryService extends AbstractDiscoveryService {
} }
private @Nullable ThingUID getThingUID(FullHueObject hueObject) { private @Nullable ThingUID getThingUID(FullHueObject hueObject) {
ThingUID bridgeUID = hueBridgeHandler.getThing().getUID(); ThingUID localBridgeUID = bridgeUID;
if (localBridgeUID != null) {
ThingTypeUID thingTypeUID = getThingTypeUID(hueObject); ThingTypeUID thingTypeUID = getThingTypeUID(hueObject);
if (thingTypeUID != null && getSupportedThingTypes().contains(thingTypeUID)) { if (thingTypeUID != null && getSupportedThingTypes().contains(thingTypeUID)) {
return new ThingUID(thingTypeUID, bridgeUID, hueObject.getId()); return new ThingUID(thingTypeUID, localBridgeUID, hueObject.getId());
} else {
return null;
} }
} }
return null;
}
private @Nullable ThingTypeUID getThingTypeUID(FullHueObject hueObject) { private @Nullable ThingTypeUID getThingTypeUID(FullHueObject hueObject) {
String thingTypeId = TYPE_TO_ZIGBEE_ID_MAP String thingTypeId = TYPE_TO_ZIGBEE_ID_MAP
@ -203,7 +233,6 @@ public class HueLightDiscoveryService extends AbstractDiscoveryService {
String modelId = sensor.getNormalizedModelID(); String modelId = sensor.getNormalizedModelID();
if (thingUID != null && thingTypeUID != null) { if (thingUID != null && thingTypeUID != null) {
ThingUID bridgeUID = hueBridgeHandler.getThing().getUID();
Map<String, Object> properties = new HashMap<>(); Map<String, Object> properties = new HashMap<>();
properties.put(SENSOR_ID, sensor.getId()); properties.put(SENSOR_ID, sensor.getId());
if (modelId != null) { if (modelId != null) {
@ -239,8 +268,9 @@ public class HueLightDiscoveryService extends AbstractDiscoveryService {
return; return;
} }
ThingUID bridgeUID = hueBridgeHandler.getThing().getUID(); ThingUID localBridgeUID = bridgeUID;
ThingUID thingUID = new ThingUID(THING_TYPE_GROUP, bridgeUID, group.getId()); if (localBridgeUID != null) {
ThingUID thingUID = new ThingUID(THING_TYPE_GROUP, localBridgeUID, group.getId());
Map<String, Object> properties = new HashMap<>(); Map<String, Object> properties = new HashMap<>();
properties.put(GROUP_ID, group.getId()); properties.put(GROUP_ID, group.getId());
@ -248,15 +278,18 @@ public class HueLightDiscoveryService extends AbstractDiscoveryService {
String name = String.format("%s (%s)", "0".equals(group.getId()) ? "All lights" : group.getName(), String name = String.format("%s (%s)", "0".equals(group.getId()) ? "All lights" : group.getName(),
group.getType()); group.getType());
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withThingType(THING_TYPE_GROUP) DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withThingType(THING_TYPE_GROUP)
.withProperties(properties).withBridge(bridgeUID).withRepresentationProperty(GROUP_ID).withLabel(name) .withProperties(properties).withBridge(localBridgeUID).withRepresentationProperty(GROUP_ID)
.build(); .withLabel(name).build();
thingDiscovered(discoveryResult); thingDiscovered(discoveryResult);
} }
}
public void removeGroupDiscovery(FullGroup group) { public void removeGroupDiscovery(FullGroup group) {
ThingUID bridgeUID = hueBridgeHandler.getThing().getUID(); ThingUID localBridgeUID = bridgeUID;
ThingUID thingUID = new ThingUID(THING_TYPE_GROUP, bridgeUID, group.getId()); if (localBridgeUID != null) {
ThingUID thingUID = new ThingUID(THING_TYPE_GROUP, localBridgeUID, group.getId());
thingRemoved(thingUID); thingRemoved(thingUID);
} }
} }
}

View File

@ -46,7 +46,7 @@ import org.openhab.binding.hue.internal.Scene;
import org.openhab.binding.hue.internal.State; import org.openhab.binding.hue.internal.State;
import org.openhab.binding.hue.internal.StateUpdate; import org.openhab.binding.hue.internal.StateUpdate;
import org.openhab.binding.hue.internal.config.HueBridgeConfig; import org.openhab.binding.hue.internal.config.HueBridgeConfig;
import org.openhab.binding.hue.internal.discovery.HueLightDiscoveryService; import org.openhab.binding.hue.internal.discovery.HueDeviceDiscoveryService;
import org.openhab.binding.hue.internal.exceptions.ApiException; import org.openhab.binding.hue.internal.exceptions.ApiException;
import org.openhab.binding.hue.internal.exceptions.DeviceOffException; import org.openhab.binding.hue.internal.exceptions.DeviceOffException;
import org.openhab.binding.hue.internal.exceptions.EntityNotAvailableException; import org.openhab.binding.hue.internal.exceptions.EntityNotAvailableException;
@ -63,6 +63,7 @@ import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail; import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.ConfigStatusBridgeHandler; import org.openhab.core.thing.binding.ConfigStatusBridgeHandler;
import org.openhab.core.thing.binding.ThingHandlerService;
import org.openhab.core.types.Command; import org.openhab.core.types.Command;
import org.openhab.core.types.StateOption; import org.openhab.core.types.StateOption;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -87,7 +88,7 @@ import org.slf4j.LoggerFactory;
@NonNullByDefault @NonNullByDefault
public class HueBridgeHandler extends ConfigStatusBridgeHandler implements HueClient { public class HueBridgeHandler extends ConfigStatusBridgeHandler implements HueClient {
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_BRIDGE); public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Set.of(THING_TYPE_BRIDGE);
private static final long BYPASS_MIN_DURATION_BEFORE_CMD = 1500L; private static final long BYPASS_MIN_DURATION_BEFORE_CMD = 1500L;
@ -102,7 +103,7 @@ public class HueBridgeHandler extends ConfigStatusBridgeHandler implements HueCl
private final Map<String, @Nullable FullSensor> lastSensorStates = new ConcurrentHashMap<>(); private final Map<String, @Nullable FullSensor> lastSensorStates = new ConcurrentHashMap<>();
private final Map<String, @Nullable FullGroup> lastGroupStates = new ConcurrentHashMap<>(); private final Map<String, @Nullable FullGroup> lastGroupStates = new ConcurrentHashMap<>();
private @Nullable HueLightDiscoveryService discoveryService; private @Nullable HueDeviceDiscoveryService discoveryService;
private final Map<String, @Nullable LightStatusListener> lightStatusListeners = new ConcurrentHashMap<>(); private final Map<String, @Nullable LightStatusListener> lightStatusListeners = new ConcurrentHashMap<>();
private final Map<String, @Nullable SensorStatusListener> sensorStatusListeners = new ConcurrentHashMap<>(); private final Map<String, @Nullable SensorStatusListener> sensorStatusListeners = new ConcurrentHashMap<>();
private final Map<String, @Nullable GroupStatusListener> groupStatusListeners = new ConcurrentHashMap<>(); private final Map<String, @Nullable GroupStatusListener> groupStatusListeners = new ConcurrentHashMap<>();
@ -183,7 +184,7 @@ public class HueBridgeHandler extends ConfigStatusBridgeHandler implements HueCl
protected void doConnectedRun() throws IOException, ApiException { protected void doConnectedRun() throws IOException, ApiException {
Map<String, @Nullable FullSensor> lastSensorStateCopy = new HashMap<>(lastSensorStates); Map<String, @Nullable FullSensor> lastSensorStateCopy = new HashMap<>(lastSensorStates);
final HueLightDiscoveryService discovery = discoveryService; final HueDeviceDiscoveryService discovery = discoveryService;
for (final FullSensor sensor : hueBridge.getSensors()) { for (final FullSensor sensor : hueBridge.getSensors()) {
String sensorId = sensor.getId(); String sensorId = sensor.getId();
@ -239,7 +240,7 @@ public class HueBridgeHandler extends ConfigStatusBridgeHandler implements HueCl
lights = hueBridge.getFullConfig().getLights(); lights = hueBridge.getFullConfig().getLights();
} }
final HueLightDiscoveryService discovery = discoveryService; final HueDeviceDiscoveryService discovery = discoveryService;
for (final FullLight fullLight : lights) { for (final FullLight fullLight : lights) {
final String lightId = fullLight.getId(); final String lightId = fullLight.getId();
@ -282,7 +283,7 @@ public class HueBridgeHandler extends ConfigStatusBridgeHandler implements HueCl
List<FullGroup> groups = hueBridge.getGroups(); List<FullGroup> groups = hueBridge.getGroups();
final HueLightDiscoveryService discovery = discoveryService; final HueDeviceDiscoveryService discovery = discoveryService;
for (final FullGroup fullGroup : groups) { for (final FullGroup fullGroup : groups) {
State groupState = new State(); State groupState = new State();
@ -409,6 +410,11 @@ public class HueBridgeHandler extends ConfigStatusBridgeHandler implements HueCl
this.stateDescriptionOptionProvider = stateDescriptionOptionProvider; this.stateDescriptionOptionProvider = stateDescriptionOptionProvider;
} }
@Override
public Collection<Class<? extends ThingHandlerService>> getServices() {
return Collections.singleton(HueDeviceDiscoveryService.class);
}
@Override @Override
public void handleCommand(ChannelUID channelUID, Command command) { public void handleCommand(ChannelUID channelUID, Command command) {
if (CHANNEL_SCENE.equals(channelUID.getId()) && command instanceof StringType) { if (CHANNEL_SCENE.equals(channelUID.getId()) && command instanceof StringType) {
@ -528,7 +534,7 @@ public class HueBridgeHandler extends ConfigStatusBridgeHandler implements HueCl
} }
} else if (e instanceof EntityNotAvailableException) { } else if (e instanceof EntityNotAvailableException) {
logger.debug("Error while accessing light: {}", e.getMessage(), e); logger.debug("Error while accessing light: {}", e.getMessage(), e);
final HueLightDiscoveryService discovery = discoveryService; final HueDeviceDiscoveryService discovery = discoveryService;
if (discovery != null) { if (discovery != null) {
discovery.removeLightDiscovery(light); discovery.removeLightDiscovery(light);
} }
@ -541,7 +547,7 @@ public class HueBridgeHandler extends ConfigStatusBridgeHandler implements HueCl
private void handleSensorUpdateException(FullSensor sensor, Throwable e) { private void handleSensorUpdateException(FullSensor sensor, Throwable e) {
if (e instanceof EntityNotAvailableException) { if (e instanceof EntityNotAvailableException) {
logger.debug("Error while accessing sensor: {}", e.getMessage(), e); logger.debug("Error while accessing sensor: {}", e.getMessage(), e);
final HueLightDiscoveryService discovery = discoveryService; final HueDeviceDiscoveryService discovery = discoveryService;
if (discovery != null) { if (discovery != null) {
discovery.removeSensorDiscovery(sensor); discovery.removeSensorDiscovery(sensor);
} }
@ -557,7 +563,7 @@ public class HueBridgeHandler extends ConfigStatusBridgeHandler implements HueCl
private void handleGroupUpdateException(FullGroup group, Throwable e) { private void handleGroupUpdateException(FullGroup group, Throwable e) {
if (e instanceof EntityNotAvailableException) { if (e instanceof EntityNotAvailableException) {
logger.debug("Error while accessing group: {}", e.getMessage(), e); logger.debug("Error while accessing group: {}", e.getMessage(), e);
final HueLightDiscoveryService discovery = discoveryService; final HueDeviceDiscoveryService discovery = discoveryService;
if (discovery != null) { if (discovery != null) {
discovery.removeGroupDiscovery(group); discovery.removeGroupDiscovery(group);
} }
@ -840,7 +846,7 @@ public class HueBridgeHandler extends ConfigStatusBridgeHandler implements HueCl
} }
@Override @Override
public boolean registerDiscoveryListener(HueLightDiscoveryService listener) { public boolean registerDiscoveryListener(HueDeviceDiscoveryService listener) {
if (discoveryService == null) { if (discoveryService == null) {
discoveryService = listener; discoveryService = listener;
getFullLights().forEach(listener::addLightDiscovery); getFullLights().forEach(listener::addLightDiscovery);
@ -964,21 +970,21 @@ public class HueBridgeHandler extends ConfigStatusBridgeHandler implements HueCl
List<FullLight> ret = withReAuthentication("search for new lights", () -> { List<FullLight> ret = withReAuthentication("search for new lights", () -> {
return hueBridge.getFullLights(); return hueBridge.getFullLights();
}); });
return ret != null ? ret : Collections.emptyList(); return ret != null ? ret : List.of();
} }
public List<FullSensor> getFullSensors() { public List<FullSensor> getFullSensors() {
List<FullSensor> ret = withReAuthentication("search for new sensors", () -> { List<FullSensor> ret = withReAuthentication("search for new sensors", () -> {
return hueBridge.getSensors(); return hueBridge.getSensors();
}); });
return ret != null ? ret : Collections.emptyList(); return ret != null ? ret : List.of();
} }
public List<FullGroup> getFullGroups() { public List<FullGroup> getFullGroups() {
List<FullGroup> ret = withReAuthentication("search for new groups", () -> { List<FullGroup> ret = withReAuthentication("search for new groups", () -> {
return hueBridge.getGroups(); return hueBridge.getGroups();
}); });
return ret != null ? ret : Collections.emptyList(); return ret != null ? ret : List.of();
} }
public void startSearch() { public void startSearch() {
@ -1024,17 +1030,13 @@ public class HueBridgeHandler extends ConfigStatusBridgeHandler implements HueCl
@Override @Override
public Collection<ConfigStatusMessage> getConfigStatus() { public Collection<ConfigStatusMessage> getConfigStatus() {
// The bridge IP address to be used for checks // The bridge IP address to be used for checks
Collection<ConfigStatusMessage> configStatusMessages;
// Check whether an IP address is provided // Check whether an IP address is provided
String ip = hueBridgeConfig.getIpAddress(); String ip = hueBridgeConfig.getIpAddress();
if (ip == null || ip.isEmpty()) { if (ip == null || ip.isEmpty()) {
configStatusMessages = Collections.singletonList(ConfigStatusMessage.Builder.error(HOST) return List.of(ConfigStatusMessage.Builder.error(HOST)
.withMessageKeySuffix(HueConfigStatusMessage.IP_ADDRESS_MISSING).withArguments(HOST).build()); .withMessageKeySuffix(HueConfigStatusMessage.IP_ADDRESS_MISSING).withArguments(HOST).build());
} else { } else {
configStatusMessages = Collections.emptyList(); return List.of();
} }
return configStatusMessages;
} }
} }

View File

@ -19,7 +19,7 @@ import org.openhab.binding.hue.internal.FullGroup;
import org.openhab.binding.hue.internal.FullLight; import org.openhab.binding.hue.internal.FullLight;
import org.openhab.binding.hue.internal.FullSensor; import org.openhab.binding.hue.internal.FullSensor;
import org.openhab.binding.hue.internal.StateUpdate; import org.openhab.binding.hue.internal.StateUpdate;
import org.openhab.binding.hue.internal.discovery.HueLightDiscoveryService; import org.openhab.binding.hue.internal.discovery.HueDeviceDiscoveryService;
/** /**
* Access to the Hue system for light handlers. * Access to the Hue system for light handlers.
@ -33,15 +33,15 @@ import org.openhab.binding.hue.internal.discovery.HueLightDiscoveryService;
public interface HueClient { public interface HueClient {
/** /**
* Register {@link HueLightDiscoveryService} to bridge handler * Register {@link HueDeviceDiscoveryService} to bridge handler
* *
* @param listener the discovery service * @param listener the discovery service
* @return {@code true} if the new discovery service is accepted * @return {@code true} if the new discovery service is accepted
*/ */
boolean registerDiscoveryListener(HueLightDiscoveryService listener); boolean registerDiscoveryListener(HueDeviceDiscoveryService listener);
/** /**
* Unregister {@link HueLightDiscoveryService} from bridge handler * Unregister {@link HueDeviceDiscoveryService} from bridge handler
* *
* @return {@code true} if the discovery service was removed * @return {@code true} if the discovery service was removed
*/ */

View File

@ -27,7 +27,7 @@ import java.util.concurrent.atomic.AtomicReference;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.openhab.binding.hue.internal.discovery.HueLightDiscoveryService; import org.openhab.binding.hue.internal.discovery.HueDeviceDiscoveryService;
import org.openhab.binding.hue.internal.handler.HueBridgeHandler; import org.openhab.binding.hue.internal.handler.HueBridgeHandler;
import org.openhab.core.config.core.Configuration; import org.openhab.core.config.core.Configuration;
import org.openhab.core.config.discovery.DiscoveryListener; import org.openhab.core.config.discovery.DiscoveryListener;
@ -44,7 +44,7 @@ import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.builder.ThingStatusInfoBuilder; import org.openhab.core.thing.binding.builder.ThingStatusInfoBuilder;
/** /**
* Tests for {@link HueLightDiscoveryService}. * Tests for {@link HueDeviceDiscoveryService}.
* *
* @author Kai Kreuzer - Initial contribution * @author Kai Kreuzer - Initial contribution
* @author Andre Fuechsel - added test 'assert start search is called()' * @author Andre Fuechsel - added test 'assert start search is called()'
@ -52,14 +52,14 @@ import org.openhab.core.thing.binding.builder.ThingStatusInfoBuilder;
* @author Denis Dudnik - switched to internally integrated source of Jue library * @author Denis Dudnik - switched to internally integrated source of Jue library
* @author Markus Rathgeb - migrated to plain Java test * @author Markus Rathgeb - migrated to plain Java test
*/ */
public class HueLightDiscoveryServiceOSGiTest extends AbstractHueOSGiTestParent { public class HueDeviceDiscoveryServiceOSGiTest extends AbstractHueOSGiTestParent {
protected HueThingHandlerFactory hueThingHandlerFactory; protected HueThingHandlerFactory hueThingHandlerFactory;
protected DiscoveryListener discoveryListener; protected DiscoveryListener discoveryListener;
protected ThingRegistry thingRegistry; protected ThingRegistry thingRegistry;
protected Bridge hueBridge; protected Bridge hueBridge;
protected HueBridgeHandler hueBridgeHandler; protected HueBridgeHandler hueBridgeHandler;
protected HueLightDiscoveryService discoveryService; protected HueDeviceDiscoveryService discoveryService;
protected final ThingTypeUID BRIDGE_THING_TYPE_UID = new ThingTypeUID("hue", "bridge"); protected final ThingTypeUID BRIDGE_THING_TYPE_UID = new ThingTypeUID("hue", "bridge");
protected final ThingUID BRIDGE_THING_UID = new ThingUID(BRIDGE_THING_TYPE_UID, "testBridge"); protected final ThingUID BRIDGE_THING_UID = new ThingUID(BRIDGE_THING_TYPE_UID, "testBridge");
@ -85,7 +85,7 @@ public class HueLightDiscoveryServiceOSGiTest extends AbstractHueOSGiTestParent
hueBridgeHandler = getThingHandler(hueBridge, HueBridgeHandler.class); hueBridgeHandler = getThingHandler(hueBridge, HueBridgeHandler.class);
assertThat(hueBridgeHandler, is(notNullValue())); assertThat(hueBridgeHandler, is(notNullValue()));
discoveryService = getService(DiscoveryService.class, HueLightDiscoveryService.class); discoveryService = getService(DiscoveryService.class, HueDeviceDiscoveryService.class);
assertThat(discoveryService, is(notNullValue())); assertThat(discoveryService, is(notNullValue()));
} }
@ -93,7 +93,7 @@ public class HueLightDiscoveryServiceOSGiTest extends AbstractHueOSGiTestParent
public void cleanUp() { public void cleanUp() {
thingRegistry.remove(BRIDGE_THING_UID); thingRegistry.remove(BRIDGE_THING_UID);
waitForAssert(() -> { waitForAssert(() -> {
assertNull(getService(DiscoveryService.class, HueLightDiscoveryService.class)); assertNull(getService(DiscoveryService.class, HueDeviceDiscoveryService.class));
}); });
} }
@ -113,6 +113,7 @@ public class HueLightDiscoveryServiceOSGiTest extends AbstractHueOSGiTestParent
public void hueLightRegistration() { public void hueLightRegistration() {
FullLight light = new FullLight(); FullLight light = new FullLight();
light.setId("1"); light.setId("1");
light.setUniqueID("AA:BB:CC:DD:EE:FF:00:11-XX");
light.setModelID("LCT001"); light.setModelID("LCT001");
light.setType("Extended color light"); light.setType("Extended color light");
@ -164,7 +165,7 @@ public class HueLightDiscoveryServiceOSGiTest extends AbstractHueOSGiTestParent
if (address.endsWith("testUserName")) { if (address.endsWith("testUserName")) {
String body = "{\"lights\":{}}"; String body = "{\"lights\":{}}";
return new Result(body, 200); return new Result(body, 200);
} else if (address.endsWith("lights") || address.endsWith("sensors")) { } else if (address.endsWith("lights") || address.endsWith("sensors") || address.endsWith("groups")) {
String body = "{}"; String body = "{}";
return new Result(body, 200); return new Result(body, 200);
} else if (address.endsWith("testUserName/config")) { } else if (address.endsWith("testUserName/config")) {