Skip to content

Commit

Permalink
[nikohomecontrol] Discovery improvements (openhab#12855)
Browse files Browse the repository at this point in the history
* Add discovery representation properties
* Recognized device types improvements
* Move discovery to thingHandlerService
* Discover multiple bridges in network
* Made device property names constants

Signed-off-by: Mark Herwege <[email protected]>
  • Loading branch information
mherwege authored and psmedley committed Feb 23, 2023
1 parent 40b4cd1 commit 89c4c58
Show file tree
Hide file tree
Showing 20 changed files with 485 additions and 321 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,9 @@ public class NikoHomeControlBindingConstants {
public static final String CONFIG_OVERRULETIME = "overruleTime";

public static final String CONFIG_ENERGYMETER_ID = "energyMeterId";

// Thing properties
public static final String PROPERTY_DEVICE_TYPE = "deviceType";
public static final String PROPERTY_DEVICE_TECHNOLOGY = "deviceTechnology";
public static final String PROPERTY_DEVICE_MODEL = "deviceModel";
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,29 +14,20 @@

import static org.openhab.binding.nikohomecontrol.internal.NikoHomeControlBindingConstants.*;

import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.nikohomecontrol.internal.discovery.NikoHomeControlDiscoveryService;
import org.openhab.binding.nikohomecontrol.internal.handler.NikoHomeControlActionHandler;
import org.openhab.binding.nikohomecontrol.internal.handler.NikoHomeControlBridgeHandler;
import org.openhab.binding.nikohomecontrol.internal.handler.NikoHomeControlBridgeHandler1;
import org.openhab.binding.nikohomecontrol.internal.handler.NikoHomeControlBridgeHandler2;
import org.openhab.binding.nikohomecontrol.internal.handler.NikoHomeControlEnergyMeterHandler;
import org.openhab.binding.nikohomecontrol.internal.handler.NikoHomeControlThermostatHandler;
import org.openhab.core.config.discovery.DiscoveryService;
import org.openhab.core.net.NetworkAddressService;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;

Expand All @@ -47,14 +38,12 @@
* @author Mark Herwege - Initial Contribution
*/

@Component(service = ThingHandlerFactory.class, configurationPid = "binding.nikohomecontrol")
@NonNullByDefault
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.nikohomecontrol")
public class NikoHomeControlHandlerFactory extends BaseThingHandlerFactory {

private @NonNullByDefault({}) NetworkAddressService networkAddressService;

private final Map<ThingUID, ServiceRegistration<?>> discoveryServiceRegs = new HashMap<>();

@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID) || BRIDGE_THING_TYPES_UIDS.contains(thingTypeUID);
Expand All @@ -63,14 +52,11 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) {
@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
if (BRIDGE_THING_TYPES_UIDS.contains(thing.getThingTypeUID())) {
NikoHomeControlBridgeHandler handler;
if (BRIDGEII_THING_TYPE.equals(thing.getThingTypeUID())) {
handler = new NikoHomeControlBridgeHandler2((Bridge) thing, networkAddressService);
return new NikoHomeControlBridgeHandler2((Bridge) thing, networkAddressService);
} else {
handler = new NikoHomeControlBridgeHandler1((Bridge) thing);
return new NikoHomeControlBridgeHandler1((Bridge) thing);
}
registerNikoHomeControlDiscoveryService(handler);
return handler;
} else if (THING_TYPE_THERMOSTAT.equals(thing.getThingTypeUID())) {
return new NikoHomeControlThermostatHandler(thing);
} else if (THING_TYPE_ENERGYMETER.equals(thing.getThingTypeUID())) {
Expand All @@ -82,29 +68,6 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return null;
}

private synchronized void registerNikoHomeControlDiscoveryService(NikoHomeControlBridgeHandler bridgeHandler) {
NikoHomeControlDiscoveryService nhcDiscoveryService = new NikoHomeControlDiscoveryService(bridgeHandler);
discoveryServiceRegs.put(bridgeHandler.getThing().getUID(), bundleContext
.registerService(DiscoveryService.class.getName(), nhcDiscoveryService, new Hashtable<>()));
nhcDiscoveryService.activate();
}

@Override
protected synchronized void removeHandler(ThingHandler thingHandler) {
if (thingHandler instanceof NikoHomeControlBridgeHandler) {
ServiceRegistration<?> serviceReg = discoveryServiceRegs.remove(thingHandler.getThing().getUID());
if (serviceReg != null) {
// remove discovery service, if bridge handler is removed
NikoHomeControlDiscoveryService service = (NikoHomeControlDiscoveryService) bundleContext
.getService(serviceReg.getReference());
serviceReg.unregister();
if (service != null) {
service.deactivate();
}
}
}
}

@Reference
protected void setNetworkAddressService(NetworkAddressService networkAddressService) {
this.networkAddressService = networkAddressService;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@
*
* @author Mark Herwege - Initial Contribution
*/
@Component(service = DiscoveryService.class, configurationPid = "discovery.nikohomecontrol")
@NonNullByDefault
@Component(service = DiscoveryService.class, configurationPid = "discovery.nikohomecontrol")
public class NikoHomeControlBridgeDiscoveryService extends AbstractDiscoveryService {

private final Logger logger = LoggerFactory.getLogger(NikoHomeControlBridgeDiscoveryService.class);
Expand All @@ -50,11 +50,11 @@ public class NikoHomeControlBridgeDiscoveryService extends AbstractDiscoveryServ

private @NonNullByDefault({}) NetworkAddressService networkAddressService;

private static final int TIMEOUT = 5;
private static final int REFRESH_INTERVAL = 60;
private static final int TIMOUT_S = 5;
private static final int REFRESH_INTERVAL_S = 60;

public NikoHomeControlBridgeDiscoveryService() {
super(NikoHomeControlBindingConstants.BRIDGE_THING_TYPES_UIDS, TIMEOUT);
super(NikoHomeControlBindingConstants.BRIDGE_THING_TYPES_UIDS, TIMOUT_S);
logger.debug("bridge discovery service started");
}

Expand All @@ -70,13 +70,18 @@ private void discoverBridge() {
}
logger.debug("discovery broadcast on {}", broadcastAddr);
NikoHomeControlDiscover nhcDiscover = new NikoHomeControlDiscover(broadcastAddr);
if (nhcDiscover.isNhcII()) {
addNhcIIBridge(nhcDiscover.getAddr(), nhcDiscover.getNhcBridgeId());
} else {
addNhcIBridge(nhcDiscover.getAddr(), nhcDiscover.getNhcBridgeId());
for (String nhcController : nhcDiscover.getNhcBridgeIds()) {
InetAddress addr = nhcDiscover.getAddr(nhcController);
if (addr != null) {
if (nhcDiscover.isNhcII(nhcController)) {
addNhcIIBridge(addr, nhcController);
} else {
addNhcIBridge(addr, nhcController);
}
}
}
} catch (IOException e) {
logger.debug("no bridge found.");
logger.debug("bridge discovery IO exception");
}
}

Expand All @@ -87,7 +92,8 @@ private void addNhcIBridge(InetAddress addr, String bridgeId) {
ThingUID uid = new ThingUID(BINDING_ID, "bridge", bridgeId);

DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(uid).withLabel(bridgeName)
.withProperty(CONFIG_HOST_NAME, addr.getHostAddress()).build();
.withProperty(CONFIG_HOST_NAME, addr.getHostAddress()).withRepresentationProperty(CONFIG_HOST_NAME)
.build();
thingDiscovered(discoveryResult);
}

Expand All @@ -98,7 +104,8 @@ private void addNhcIIBridge(InetAddress addr, String bridgeId) {
ThingUID uid = new ThingUID(BINDING_ID, "bridge2", bridgeId);

DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(uid).withLabel(bridgeName)
.withProperty(CONFIG_HOST_NAME, addr.getHostAddress()).build();
.withProperty(CONFIG_HOST_NAME, addr.getHostAddress()).withRepresentationProperty(CONFIG_HOST_NAME)
.build();
thingDiscovered(discoveryResult);
}

Expand All @@ -115,10 +122,10 @@ protected synchronized void stopScan() {

@Override
protected void startBackgroundDiscovery() {
logger.debug("Start background bridge discovery");
logger.debug("Start bridge background discovery");
ScheduledFuture<?> job = nhcDiscoveryJob;
if (job == null || job.isCancelled()) {
nhcDiscoveryJob = scheduler.scheduleWithFixedDelay(this::discoverBridge, 0, REFRESH_INTERVAL,
nhcDiscoveryJob = scheduler.scheduleWithFixedDelay(this::discoverBridge, 0, REFRESH_INTERVAL_S,
TimeUnit.SECONDS);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@

import static org.openhab.binding.nikohomecontrol.internal.NikoHomeControlBindingConstants.*;

import java.util.Date;
import java.time.Instant;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
Expand All @@ -27,6 +29,8 @@
import org.openhab.core.config.discovery.AbstractDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
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.LoggerFactory;

Expand All @@ -37,43 +41,52 @@
* @author Mark Herwege - Initial Contribution
*/
@NonNullByDefault
public class NikoHomeControlDiscoveryService extends AbstractDiscoveryService {
public class NikoHomeControlDiscoveryService extends AbstractDiscoveryService implements ThingHandlerService {

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

private static final int TIMEOUT = 5;
private volatile @Nullable ScheduledFuture<?> nhcDiscoveryJob;

private ThingUID bridgeUID;
private NikoHomeControlBridgeHandler handler;
private static final int TIMEOUT_S = 5;
private static final int INITIAL_DELAY_S = 5; // initial delay for polling to allow time for initial request to NHC
// controller to complete
private static final int REFRESH_INTERVAL_S = 60;

public NikoHomeControlDiscoveryService(NikoHomeControlBridgeHandler handler) {
super(SUPPORTED_THING_TYPES_UIDS, TIMEOUT, false);
logger.debug("discovery service {}", handler);
bridgeUID = handler.getThing().getUID();
this.handler = handler;
private @Nullable ThingUID bridgeUID;
private @Nullable NikoHomeControlBridgeHandler handler;

public NikoHomeControlDiscoveryService() {
super(SUPPORTED_THING_TYPES_UIDS, TIMEOUT_S, true);
logger.debug("device discovery service started");
}

@Override
public void activate() {
handler.setNhcDiscovery(this);
startBackgroundDiscovery();
}

@Override
public void deactivate() {
removeOlderResults(new Date().getTime());
handler.setNhcDiscovery(null);
removeOlderResults(Instant.now().toEpochMilli());
super.deactivate();
}

/**
* Discovers devices connected to a Niko Home Control controller
*/
public void discoverDevices() {
NikoHomeControlCommunication nhcComm = handler.getCommunication();
NikoHomeControlBridgeHandler bridgeHandler = handler;
if (bridgeHandler == null) {
return;
}

NikoHomeControlCommunication nhcComm = bridgeHandler.getCommunication();

if ((nhcComm == null) || !nhcComm.communicationActive()) {
logger.warn("not connected");
return;
}
logger.debug("getting devices on {}", handler.getThing().getUID().getId());
logger.debug("getting devices on {}", bridgeHandler.getThing().getUID().getId());

Map<String, NhcAction> actions = nhcComm.getActions();

Expand All @@ -83,20 +96,21 @@ public void discoverDevices() {

switch (nhcAction.getType()) {
case TRIGGER:
addActionDevice(new ThingUID(THING_TYPE_PUSHBUTTON, handler.getThing().getUID(), actionId),
addActionDevice(new ThingUID(THING_TYPE_PUSHBUTTON, bridgeHandler.getThing().getUID(), actionId),
actionId, thingName, thingLocation);
break;
case RELAY:
addActionDevice(new ThingUID(THING_TYPE_ON_OFF_LIGHT, handler.getThing().getUID(), actionId),
addActionDevice(new ThingUID(THING_TYPE_ON_OFF_LIGHT, bridgeHandler.getThing().getUID(), actionId),
actionId, thingName, thingLocation);
break;
case DIMMER:
addActionDevice(new ThingUID(THING_TYPE_DIMMABLE_LIGHT, handler.getThing().getUID(), actionId),
addActionDevice(
new ThingUID(THING_TYPE_DIMMABLE_LIGHT, bridgeHandler.getThing().getUID(), actionId),
actionId, thingName, thingLocation);
break;
case ROLLERSHUTTER:
addActionDevice(new ThingUID(THING_TYPE_BLIND, handler.getThing().getUID(), actionId), actionId,
thingName, thingLocation);
addActionDevice(new ThingUID(THING_TYPE_BLIND, bridgeHandler.getThing().getUID(), actionId),
actionId, thingName, thingLocation);
break;
default:
logger.debug("unrecognized action type {} for {} {}", nhcAction.getType(), actionId, thingName);
Expand All @@ -108,22 +122,24 @@ public void discoverDevices() {
thermostats.forEach((thermostatId, nhcThermostat) -> {
String thingName = nhcThermostat.getName();
String thingLocation = nhcThermostat.getLocation();
addThermostatDevice(new ThingUID(THING_TYPE_THERMOSTAT, handler.getThing().getUID(), thermostatId),
addThermostatDevice(new ThingUID(THING_TYPE_THERMOSTAT, bridgeHandler.getThing().getUID(), thermostatId),
thermostatId, thingName, thingLocation);
});

Map<String, NhcEnergyMeter> energyMeters = nhcComm.getEnergyMeters();

energyMeters.forEach((energyMeterId, nhcEnergyMeter) -> {
String thingName = nhcEnergyMeter.getName();
addEnergyMeterDevice(new ThingUID(THING_TYPE_ENERGYMETER, handler.getThing().getUID(), energyMeterId),
energyMeterId, thingName);
String thingLocation = nhcEnergyMeter.getLocation();
addEnergyMeterDevice(new ThingUID(THING_TYPE_ENERGYMETER, bridgeHandler.getThing().getUID(), energyMeterId),
energyMeterId, thingName, thingLocation);
});
}

private void addActionDevice(ThingUID uid, String actionId, String thingName, @Nullable String thingLocation) {
DiscoveryResultBuilder discoveryResultBuilder = DiscoveryResultBuilder.create(uid).withBridge(bridgeUID)
.withLabel(thingName).withProperty(CONFIG_ACTION_ID, actionId);
.withLabel(thingName).withProperty(CONFIG_ACTION_ID, actionId)
.withRepresentationProperty(CONFIG_ACTION_ID);
if (thingLocation != null) {
discoveryResultBuilder.withProperty("Location", thingLocation);
}
Expand All @@ -133,16 +149,22 @@ private void addActionDevice(ThingUID uid, String actionId, String thingName, @N
private void addThermostatDevice(ThingUID uid, String thermostatId, String thingName,
@Nullable String thingLocation) {
DiscoveryResultBuilder discoveryResultBuilder = DiscoveryResultBuilder.create(uid).withBridge(bridgeUID)
.withLabel(thingName).withProperty(CONFIG_THERMOSTAT_ID, thermostatId);
.withLabel(thingName).withProperty(CONFIG_THERMOSTAT_ID, thermostatId)
.withRepresentationProperty(CONFIG_THERMOSTAT_ID);
if (thingLocation != null) {
discoveryResultBuilder.withProperty("Location", thingLocation);
}
thingDiscovered(discoveryResultBuilder.build());
}

private void addEnergyMeterDevice(ThingUID uid, String energyMeterId, String thingName) {
private void addEnergyMeterDevice(ThingUID uid, String energyMeterId, String thingName,
@Nullable String thingLocation) {
DiscoveryResultBuilder discoveryResultBuilder = DiscoveryResultBuilder.create(uid).withBridge(bridgeUID)
.withLabel(thingName).withProperty(CONFIG_ENERGYMETER_ID, energyMeterId);
.withLabel(thingName).withProperty(CONFIG_ENERGYMETER_ID, energyMeterId)
.withRepresentationProperty(CONFIG_ENERGYMETER_ID);
if (thingLocation != null) {
discoveryResultBuilder.withProperty("Location", thingLocation);
}
thingDiscovered(discoveryResultBuilder.build());
}

Expand All @@ -156,4 +178,37 @@ protected synchronized void stopScan() {
super.stopScan();
removeOlderResults(getTimestampOfLastScan());
}

@Override
protected void startBackgroundDiscovery() {
logger.debug("Start device background discovery");
ScheduledFuture<?> job = nhcDiscoveryJob;
if (job == null || job.isCancelled()) {
nhcDiscoveryJob = scheduler.scheduleWithFixedDelay(this::discoverDevices, INITIAL_DELAY_S,
REFRESH_INTERVAL_S, TimeUnit.SECONDS);
}
}

@Override
protected void stopBackgroundDiscovery() {
logger.debug("Stop device background discovery");
ScheduledFuture<?> job = nhcDiscoveryJob;
if (job != null && !job.isCancelled()) {
job.cancel(true);
nhcDiscoveryJob = null;
}
}

@Override
public void setThingHandler(@Nullable ThingHandler handler) {
if (handler instanceof NikoHomeControlBridgeHandler) {
this.handler = (NikoHomeControlBridgeHandler) handler;
bridgeUID = handler.getThing().getUID();
}
}

@Override
public @Nullable ThingHandler getThingHandler() {
return handler;
}
}
Loading

0 comments on commit 89c4c58

Please sign in to comment.