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";
}
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;
* @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);
@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())) {
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;
*
* @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);
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");
}
}
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");
}
}
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);
}
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);
}
@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);
}
}
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;
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;
* @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();
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);
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);
});
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);
}
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());
}
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;
+ }
}
nhcAction.setEventHandler(this);
- updateProperties();
+ updateProperties(nhcAction);
String actionLocation = nhcAction.getLocation();
if (thing.getLocation() == null) {
});
}
- private void updateProperties() {
- NhcAction nhcAction = this.nhcAction;
- if (nhcAction == null) {
- logger.debug("action with ID {} not initialized", actionId);
- return;
- }
-
+ private void updateProperties(NhcAction nhcAction) {
Map<String, String> properties = new HashMap<>();
+
properties.put("type", String.valueOf(nhcAction.getType()));
if (getThing().getThingTypeUID() == THING_TYPE_BLIND) {
properties.put("timeToOpen", String.valueOf(nhcAction.getOpenTime()));
if (nhcAction instanceof NhcAction2) {
NhcAction2 action = (NhcAction2) nhcAction;
- properties.put("model", action.getModel());
- properties.put("technology", action.getTechnology());
+ properties.put(PROPERTY_DEVICE_TYPE, action.getDeviceType());
+ properties.put(PROPERTY_DEVICE_TECHNOLOGY, action.getDeviceTechnology());
+ properties.put(PROPERTY_DEVICE_MODEL, action.getDeviceModel());
}
thing.setProperties(properties);
import java.net.InetAddress;
import java.net.UnknownHostException;
+import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Set;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* {@link NikoHomeControlBridgeHandler} is an abstract class representing a handler to all different interfaces to the
* Niko Home Control System. {@link NikoHomeControlBridgeHandler1} or {@link NikoHomeControlBridgeHandler2} should be
- * used for the respective
- * version of Niko Home Control.
+ * used for the respective version of Niko Home Control.
*
* @author Mark Herwege - Initial Contribution
*/
private final Logger logger = LoggerFactory.getLogger(NikoHomeControlBridgeHandler.class);
- protected @NonNullByDefault({}) NikoHomeControlBridgeConfig config;
-
protected @Nullable NikoHomeControlCommunication nhcComm;
private volatile @Nullable ScheduledFuture<?> refreshTimer;
- protected volatile @Nullable NikoHomeControlDiscoveryService nhcDiscovery;
-
public NikoHomeControlBridgeHandler(Bridge nikoHomeControlBridge) {
super(nikoHomeControlBridge);
}
updateStatus(ThingStatus.ONLINE);
- int refreshInterval = config.refresh;
+ int refreshInterval = getConfig().as(NikoHomeControlBridgeConfig.class).refresh;
setupRefreshTimer(refreshInterval);
-
- NikoHomeControlDiscoveryService discovery = nhcDiscovery;
- if (discovery != null) {
- discovery.discoverDevices();
- } else {
- logger.debug("cannot discover devices, discovery service not started");
- }
});
}
/**
* Schedule future communication refresh.
*
- * @param interval_config Time before refresh in minutes.
+ * @param refreshInterval Time before refresh in minutes.
*/
private void setupRefreshTimer(int refreshInterval) {
ScheduledFuture<?> timer = refreshTimer;
public void controllerOnline() {
bridgeOnline();
- int refreshInterval = config.refresh;
+ int refreshInterval = getConfig().as(NikoHomeControlBridgeConfig.class).refresh;
if (refreshTimer == null) {
setupRefreshTimer(refreshInterval);
}
}
Configuration configuration = editConfiguration();
- for (Entry<String, Object> configurationParmeter : configurationParameters.entrySet()) {
- configuration.put(configurationParmeter.getKey(), configurationParmeter.getValue());
+ for (Entry<String, Object> configurationParameter : configurationParameters.entrySet()) {
+ configuration.put(configurationParameter.getKey(), configurationParameter.getValue());
}
updateConfiguration(configuration);
- setConfig();
-
scheduler.submit(() -> {
comm.restartCommunication();
if (!comm.communicationActive()) {
updateStatus(ThingStatus.ONLINE);
- int refreshInterval = config.refresh;
+ int refreshInterval = getConfig().as(NikoHomeControlBridgeConfig.class).refresh;
setupRefreshTimer(refreshInterval);
});
}
- /**
- * Set discovery service handler to be able to start discovery after bridge initialization.
- *
- * @param nhcDiscovery
- */
- public void setNhcDiscovery(@Nullable NikoHomeControlDiscoveryService nhcDiscovery) {
- this.nhcDiscovery = nhcDiscovery;
- }
-
@Override
public void alarmEvent(String alarmText) {
logger.debug("triggering alarm channel with {}", alarmText);
@Override
public @Nullable InetAddress getAddr() {
InetAddress addr = null;
+ NikoHomeControlBridgeConfig config = getConfig().as(NikoHomeControlBridgeConfig.class);
try {
addr = InetAddress.getByName(config.addr);
} catch (UnknownHostException e) {
@Override
public int getPort() {
- return config.port;
+ return getConfig().as(NikoHomeControlBridgeConfig.class).port;
}
- protected synchronized void setConfig() {
- config = getConfig().as(NikoHomeControlBridgeConfig.class);
+ @Override
+ public Collection<Class<? extends ThingHandlerService>> getServices() {
+ return Set.of(NikoHomeControlDiscoveryService.class);
}
}
public void initialize() {
logger.debug("initializing bridge handler");
- setConfig();
InetAddress addr = getAddr();
int port = getPort();
public void initialize() {
logger.debug("initializing NHC II bridge handler");
- setConfig();
-
Date expiryDate = getTokenExpiryDate();
if (expiryDate == null) {
if (getToken().isEmpty()) {
@Override
public String getProfile() {
- return ((NikoHomeControlBridgeConfig2) config).profile;
+ return getConfig().as(NikoHomeControlBridgeConfig2.class).profile;
}
@Override
public String getToken() {
- String token = ((NikoHomeControlBridgeConfig2) config).password;
+ String token = getConfig().as(NikoHomeControlBridgeConfig2.class).password;
if (token.isEmpty()) {
logger.debug("no JWT token set.");
}
return null;
}
-
- @Override
- protected synchronized void setConfig() {
- config = getConfig().as(NikoHomeControlBridgeConfig2.class);
- }
}
*/
package org.openhab.binding.nikohomecontrol.internal.handler;
-import static org.openhab.binding.nikohomecontrol.internal.NikoHomeControlBindingConstants.CHANNEL_POWER;
+import static org.openhab.binding.nikohomecontrol.internal.NikoHomeControlBindingConstants.*;
import static org.openhab.core.types.RefreshType.REFRESH;
import java.util.HashMap;
nhcEnergyMeter.setEventHandler(this);
- updateProperties();
+ updateProperties(nhcEnergyMeter);
+
+ String location = nhcEnergyMeter.getLocation();
+ if (thing.getLocation() == null) {
+ thing.setLocation(location);
+ }
// Subscribing to power readings starts an intensive data flow, therefore only do it when there is an item
// linked to the channel
}
}
- private void updateProperties() {
+ private void updateProperties(NhcEnergyMeter nhcEnergyMeter) {
Map<String, String> properties = new HashMap<>();
if (nhcEnergyMeter instanceof NhcEnergyMeter2) {
NhcEnergyMeter2 energyMeter = (NhcEnergyMeter2) nhcEnergyMeter;
- properties.put("model", energyMeter.getModel());
- properties.put("technology", energyMeter.getTechnology());
+ properties.put(PROPERTY_DEVICE_TYPE, energyMeter.getDeviceType());
+ properties.put(PROPERTY_DEVICE_TECHNOLOGY, energyMeter.getDeviceTechnology());
+ properties.put(PROPERTY_DEVICE_MODEL, energyMeter.getDeviceModel());
}
thing.setProperties(properties);
nhcThermostat.setEventHandler(this);
- updateProperties();
+ updateProperties(nhcThermostat);
String thermostatLocation = nhcThermostat.getLocation();
if (thing.getLocation() == null) {
});
}
- private void updateProperties() {
+ private void updateProperties(NhcThermostat nhcThermostat) {
Map<String, String> properties = new HashMap<>();
if (nhcThermostat instanceof NhcThermostat2) {
NhcThermostat2 thermostat = (NhcThermostat2) nhcThermostat;
- properties.put("model", thermostat.getModel());
- properties.put("technology", thermostat.getTechnology());
+ properties.put(PROPERTY_DEVICE_TYPE, thermostat.getDeviceType());
+ properties.put(PROPERTY_DEVICE_TECHNOLOGY, thermostat.getDeviceTechnology());
+ properties.put(PROPERTY_DEVICE_MODEL, thermostat.getDeviceModel());
}
thing.setProperties(properties);
}
/**
- * Get the id of the action.
+ * Get id of action.
*
- * @return the id
+ * @return id
*/
public String getId() {
return id;
return name;
}
+ /**
+ * Set name of action.
+ *
+ * @param name action name
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
/**
* Get type of action identified.
* <p>
return location;
}
+ /**
+ * Set location name of action.
+ *
+ * @param location action location name
+ */
+ public void setLocation(@Nullable String location) {
+ this.location = location;
+ }
+
/**
* Get state of action.
* <p>
protected String id;
protected String name;
+ protected @Nullable String location;
// This can be null as long as we do not receive power readings
protected volatile @Nullable Integer power = null;
private @Nullable NhcEnergyMeterEvent eventHandler;
- protected NhcEnergyMeter(String id, String name, NikoHomeControlCommunication nhcComm) {
+ protected NhcEnergyMeter(String id, String name, @Nullable String location, NikoHomeControlCommunication nhcComm) {
this.id = id;
this.name = name;
+ this.location = location;
this.nhcComm = nhcComm;
}
}
/**
- * Get the id of the energyMeters meter.
+ * Get id of meter.
*
- * @return the id
+ * @return id
*/
public String getId() {
return id;
}
/**
- * Get name of the energyMeters meter.
+ * Get name of meter.
*
- * @return energyMeters meter name
+ * @return energyMeter name
*/
public String getName() {
return name;
}
+ /**
+ * Set name of meter.
+ *
+ * @param name meter name
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Get location name of meter.
+ *
+ * @return location energyMeter location
+ */
+ public @Nullable String getLocation() {
+ return location;
+ }
+
+ /**
+ * Set location name of meter.
+ *
+ * @param location meter location name
+ */
+ public void setLocation(@Nullable String location) {
+ this.location = location;
+ }
+
/**
* @return the power in W (positive for consumption, negative for production), return null if no reading received
* yet
}
/**
- * Get the id of the thermostat.
+ * Get id of the thermostat.
*
- * @return the id
+ * @return id
*/
public String getId() {
return id;
}
/**
- * Get location name of action.
+ * Set name of thermostat.
+ *
+ * @param name thermostat name
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Get location name of thermostat.
*
* @return location name
*/
return location;
}
+ /**
+ * Set location name of thermostat.
+ *
+ * @param location thermostat location name
+ */
+ public void setLocation(@Nullable String location) {
+ this.location = location;
+ }
+
/**
* Get measured temperature.
*
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
+import java.net.SocketTimeoutException;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.util.HexUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private final Logger logger = LoggerFactory.getLogger(NikoHomeControlDiscover.class);
- private InetAddress addr;
- private String nhcBridgeId = "";
- private boolean isNhcII;
+ private List<String> nhcBridgeIds = new ArrayList<>();
+ private Map<String, InetAddress> addr = new HashMap<>();
+ private Map<String, Boolean> isNhcII = new HashMap<>();
/**
- * Discover a Niko Home Control IP interface by broadcasting UDP packet 0x44 to port 10000. The IP interface will
- * reply. The address of the IP interface is than derived from that response.
+ * Discover the list of Niko Home Control IP interfaces by broadcasting UDP packet 0x44 to port 10000. The IP
+ * interface will reply. The address of the IP interface is than derived from that response.
*
* @param broadcast Broadcast address of the network
* @throws IOException
datagramSocket.setBroadcast(true);
datagramSocket.setSoTimeout(500);
datagramSocket.send(discoveryPacket);
- while (true) {
- datagramSocket.receive(packet);
- logger.trace("bridge discovery response {}",
- HexUtils.bytesToHex(Arrays.copyOf(packet.getData(), packet.getLength())));
- if (isNhc(packet)) {
- break;
+ try {
+ while (true) {
+ datagramSocket.receive(packet);
+ logger.trace("bridge discovery response {}",
+ HexUtils.bytesToHex(Arrays.copyOf(packet.getData(), packet.getLength())));
+ if (isNhcController(packet)) {
+ String bridgeId = setNhcBridgeId(packet);
+ setIsNhcII(bridgeId, packet);
+ setAddr(bridgeId, packet);
+ logger.debug("IP address is {}, unique ID is {}", addr, bridgeId);
+ }
}
+ } catch (SocketTimeoutException e) {
+ // all received, continue
}
- addr = packet.getAddress();
- setNhcBridgeId(packet);
- setIsNhcII(packet);
- logger.debug("IP address is {}, unique ID is {}", addr, nhcBridgeId);
}
}
/**
- * @return the addr
+ * @return the discovered nhcBridgeIds
*/
- public InetAddress getAddr() {
- return addr;
+ public List<String> getNhcBridgeIds() {
+ return nhcBridgeIds;
}
/**
- * @return the nhcBridgeId
+ * @param bridgeId discovered bridgeId
+ * @return the addr, null if not in the list of discovered bridgeId's
*/
- public String getNhcBridgeId() {
- return nhcBridgeId;
+ public @Nullable InetAddress getAddr(String bridgeId) {
+ return addr.get(bridgeId);
}
/**
* @param packet
* @return true if packet is from a Niko Home Control controller
*/
- private boolean isNhc(DatagramPacket packet) {
+ private boolean isNhcController(DatagramPacket packet) {
byte[] packetData = packet.getData();
- return ((packet.getLength() > 2) && (packetData[0] == 0x44));
+ boolean isNhc = (packet.getLength() > 2) && (packetData[0] == 0x44);
+ // filter response from Gen1 touchscreens
+ boolean isController = isNhc && (packetData[1] == 0x3b) || (packetData[1] == 0x0c) || (packetData[1] == 0x0e);
+ if (!isController) {
+ logger.trace("not a NHC controller");
+ }
+ return isController;
}
/**
*
* @param packet
*/
- private void setNhcBridgeId(DatagramPacket packet) {
+ private String setNhcBridgeId(DatagramPacket packet) {
byte[] packetData = packet.getData();
int packetLength = packet.getLength();
packetLength = packetLength > 6 ? 6 : packetLength;
for (int i = 0; i < packetLength; i++) {
sb.append(String.format("%02x", packetData[i]));
}
- nhcBridgeId = sb.toString();
+ String bridgeId = sb.toString();
+ nhcBridgeIds.add(bridgeId);
+ return bridgeId;
}
/**
* Checks if this is a NHC II Connected Controller
*
+ * @param bridgeId
* @param packet
*/
- private void setIsNhcII(DatagramPacket packet) {
+ private void setIsNhcII(String bridgeId, DatagramPacket packet) {
byte[] packetData = packet.getData();
int packetLength = packet.getLength();
// The 16th byte in the packet is 2 for a NHC II Connected Controller
if ((packetLength >= 16) && (packetData[15] >= 2)) {
- isNhcII = true;
+ isNhcII.put(bridgeId, true);
} else {
- isNhcII = false;
+ isNhcII.put(bridgeId, false);
}
}
+ /**
+ * Sets the IP address retrieved from the packet response
+ *
+ * @param bridgeId
+ * @param packet
+ */
+ private void setAddr(String bridgeId, DatagramPacket packet) {
+ addr.put(bridgeId, packet.getAddress());
+ }
+
/**
* Test if the installation is a Niko Home Control II installation
*
+ * @param bridgeId
* @return true if this is a Niko Home Control II installation
*/
- public boolean isNhcII() {
- return isNhcII;
+ public boolean isNhcII(String bridgeId) {
+ return isNhcII.getOrDefault(bridgeId, false);
}
}
String value3 = action.get("value3");
int openTime = ((value3 == null) || value3.isEmpty() ? 0 : Integer.parseInt(value3));
+ String name = action.get("name");
+ if (name == null) {
+ logger.debug("name not found in action {}", action);
+ continue;
+ }
+ String type = Optional.ofNullable(action.get("type")).orElse("");
+ ActionType actionType = ActionType.GENERIC;
+ switch (type) {
+ case "0":
+ actionType = ActionType.TRIGGER;
+ break;
+ case "1":
+ actionType = ActionType.RELAY;
+ break;
+ case "2":
+ actionType = ActionType.DIMMER;
+ break;
+ case "4":
+ case "5":
+ actionType = ActionType.ROLLERSHUTTER;
+ break;
+ default:
+ logger.debug("unknown action type {} for action {}", type, id);
+ continue;
+ }
+ String locationId = action.get("location");
+ String location = "";
+ if (locationId != null && !locationId.isEmpty()) {
+ location = locations.getOrDefault(locationId, new NhcLocation1("")).getName();
+ }
if (!actions.containsKey(id)) {
// Initial instantiation of NhcAction class for action object
- String name = action.get("name");
- if (name == null) {
- logger.debug("name not found in action {}", action);
- continue;
- }
- String type = Optional.ofNullable(action.get("type")).orElse("");
- ActionType actionType = ActionType.GENERIC;
- switch (type) {
- case "0":
- actionType = ActionType.TRIGGER;
- break;
- case "1":
- actionType = ActionType.RELAY;
- break;
- case "2":
- actionType = ActionType.DIMMER;
- break;
- case "4":
- case "5":
- actionType = ActionType.ROLLERSHUTTER;
- break;
- default:
- logger.debug("unknown action type {} for action {}", type, id);
- continue;
- }
- String locationId = action.get("location");
- String location = "";
- if (locationId != null && !locationId.isEmpty()) {
- location = locations.getOrDefault(locationId, new NhcLocation1("")).getName();
- }
NhcAction nhcAction = new NhcAction1(id, name, actionType, location, this, scheduler);
if (actionType == ActionType.ROLLERSHUTTER) {
nhcAction.setShutterTimes(openTime, closeTime);
nhcAction.setState(state);
actions.put(id, nhcAction);
} else {
- // Action object already exists, so only update state.
+ // Action object already exists, so only update state, name and location.
// If we would re-instantiate action, we would lose pointer back from action to thing handler that was
// set in thing handler initialize().
NhcAction nhcAction = actions.get(id);
if (nhcAction != null) {
+ nhcAction.setName(name);
+ nhcAction.setLocation(location);
nhcAction.setState(state);
}
}
// measured
int demand = (mode != 3) ? (setpoint > measured ? 1 : (setpoint < measured ? -1 : 0)) : 0;
+ String name = thermostat.get("name");
+ String locationId = thermostat.get("location");
+ NhcLocation1 nhcLocation = null;
+ if (!((locationId == null) || locationId.isEmpty())) {
+ nhcLocation = locations.get(locationId);
+ }
+ String location = (nhcLocation != null) ? nhcLocation.getName() : null;
NhcThermostat t = thermostats.computeIfAbsent(id, i -> {
// Initial instantiation of NhcThermostat class for thermostat object
- String name = thermostat.get("name");
- String locationId = thermostat.get("location");
- String location = "";
- if (!((locationId == null) || locationId.isEmpty())) {
- NhcLocation1 nhcLocation = locations.get(locationId);
- if (nhcLocation != null) {
- location = nhcLocation.getName();
- }
- }
if (name != null) {
return new NhcThermostat1(i, name, location, this);
}
throw new IllegalArgumentException();
});
if (t != null) {
+ if (name != null) {
+ t.setName(name);
+ }
+ t.setLocation(location);
t.updateState(measured, setpoint, mode, overrule, overruletime, ecosave, demand);
}
} catch (IllegalArgumentException e) {
private final Logger logger = LoggerFactory.getLogger(NhcAction2.class);
private volatile boolean booleanState;
- private String model;
- private String technology;
+ private String deviceType;
+ private String deviceTechnology;
+ private String deviceModel;
- NhcAction2(String id, String name, String model, String technology, ActionType type, @Nullable String location,
- NikoHomeControlCommunication nhcComm) {
+ NhcAction2(String id, String name, String deviceType, String deviceTechnology, String deviceModel,
+ @Nullable String location, ActionType type, NikoHomeControlCommunication nhcComm) {
super(id, name, type, location, nhcComm);
- this.model = model;
- this.technology = technology;
+ this.deviceType = deviceType;
+ this.deviceTechnology = deviceTechnology;
+ this.deviceModel = deviceModel;
}
/**
logger.debug("execute action {} of type {} for {}", command, type, id);
String cmd;
- if ("flag".equals(model)) {
+ if ("flag".equals(deviceModel)) {
cmd = NHCON.equals(command) ? NHCTRUE : NHCFALSE;
} else {
cmd = command;
}
/**
- * @return model as returned from Niko Home Control
+ * @return type as returned from Niko Home Control
*/
- public String getModel() {
- return model;
+ public String getDeviceType() {
+ return deviceType;
}
/**
* @return technology as returned from Niko Home Control
*/
- public String getTechnology() {
- return technology;
+ public String getDeviceTechnology() {
+ return deviceTechnology;
+ }
+
+ /**
+ * @return model as returned from Niko Home Control
+ */
+ public String getDeviceModel() {
+ return deviceModel;
}
}
@Nullable
String electricalPower;
@Nullable
+ String electricalPowerToGrid;
+ @Nullable
+ String electricalPowerFromGrid;
+ @Nullable
+ String electricalPowerProduction;
+ @Nullable
+ String electricalPowerSelfConsumption;
+ @Nullable
+ String electricalPowerConsumption;
+ @Nullable
+ String electricalPowerProductionThresholdExceeded;
+ @Nullable
String reportInstantUsage;
// fields for access control
@Nullable
private ScheduledExecutorService scheduler;
private volatile @Nullable ScheduledFuture<?> restartTimer;
- private String model;
- private String technology;
+ private String deviceType;
+ private String deviceTechnology;
+ private String deviceModel;
- protected NhcEnergyMeter2(String id, String name, String model, String technology,
- NikoHomeControlCommunication nhcComm, ScheduledExecutorService scheduler) {
- super(id, name, nhcComm);
- this.model = model;
- this.technology = technology;
+ protected NhcEnergyMeter2(String id, String name, String deviceType, String deviceTechnology, String deviceModel,
+ @Nullable String location, NikoHomeControlCommunication nhcComm, ScheduledExecutorService scheduler) {
+ super(id, name, location, nhcComm);
+ this.deviceType = deviceType;
+ this.deviceTechnology = deviceTechnology;
+ this.deviceModel = deviceModel;
this.scheduler = scheduler;
}
}
/**
- * @return model as returned from Niko Home Control
+ * @return type as returned from Niko Home Control
*/
- public String getModel() {
- return model;
+ public String getDeviceType() {
+ return deviceType;
}
/**
* @return technology as returned from Niko Home Control
*/
- public String getTechnology() {
- return technology;
+ public String getDeviceTechnology() {
+ return deviceTechnology;
+ }
+
+ /**
+ * @return model as returned from Niko Home Control
+ */
+ public String getDeviceModel() {
+ return deviceModel;
}
}
private final Logger logger = LoggerFactory.getLogger(NhcThermostat2.class);
- private String model;
- private String technology;
+ private String deviceType;
+ private String deviceTechnology;
+ private String deviceModel;
- protected NhcThermostat2(String id, String name, String model, String technology, @Nullable String location,
- NikoHomeControlCommunication nhcComm) {
+ protected NhcThermostat2(String id, String name, String deviceType, String deviceTechnology, String deviceModel,
+ @Nullable String location, NikoHomeControlCommunication nhcComm) {
super(id, name, location, nhcComm);
- this.model = model;
- this.technology = technology;
+ this.deviceType = deviceType;
+ this.deviceTechnology = deviceTechnology;
+ this.deviceModel = deviceModel;
}
@Override
}
/**
- * @return model as returned from Niko Home Control
+ * @return type as returned from Niko Home Control
*/
- public String getModel() {
- return model;
+ public String getDeviceType() {
+ return deviceType;
}
/**
* @return technology as returned from Niko Home Control
*/
- public String getTechnology() {
- return technology;
+ public String getDeviceTechnology() {
+ return deviceTechnology;
+ }
+
+ /**
+ * @return model as returned from Niko Home Control
+ */
+ public String getDeviceModel() {
+ return deviceModel;
}
}
}
if ("action".equals(device.type) || "virtual".equals(device.type)) {
- if (!actions.containsKey(device.uuid)) {
- logger.debug("adding action device {}, {}", device.uuid, device.name);
-
- ActionType actionType;
- switch (device.model) {
- case "generic":
- case "pir":
- case "simulation":
- case "comfort":
- case "alarms":
- case "alloff":
- case "overallcomfort":
- case "garagedoor":
- actionType = ActionType.TRIGGER;
- break;
- case "light":
- case "socket":
- case "switched-generic":
- case "switched-fan":
- case "flag":
- actionType = ActionType.RELAY;
- break;
- case "dimmer":
- actionType = ActionType.DIMMER;
- break;
- case "rolldownshutter":
- case "sunblind":
- case "venetianblind":
- case "gate":
- actionType = ActionType.ROLLERSHUTTER;
- break;
- default:
- actionType = ActionType.GENERIC;
- logger.debug("device model {} not recognised, default to GENERIC action", device.model);
- }
+ ActionType actionType;
+ switch (device.model) {
+ case "generic":
+ case "pir":
+ case "simulation":
+ case "comfort":
+ case "alarms":
+ case "alloff":
+ case "overallcomfort":
+ case "garagedoor":
+ actionType = ActionType.TRIGGER;
+ break;
+ case "light":
+ case "socket":
+ case "switched-generic":
+ case "switched-fan":
+ case "flag":
+ actionType = ActionType.RELAY;
+ break;
+ case "dimmer":
+ actionType = ActionType.DIMMER;
+ break;
+ case "rolldownshutter":
+ case "sunblind":
+ case "venetianblind":
+ case "gate":
+ actionType = ActionType.ROLLERSHUTTER;
+ break;
+ default:
+ actionType = ActionType.GENERIC;
+ logger.debug("device type {} and model {} not recognised for {}, {}, ignoring", device.type,
+ device.model, device.uuid, device.name);
+ return;
+ }
- NhcAction2 nhcAction = new NhcAction2(device.uuid, device.name, device.model, device.technology,
- actionType, location, this);
- actions.put(device.uuid, nhcAction);
+ NhcAction nhcAction = actions.get(device.uuid);
+ if (nhcAction != null) {
+ // update name and location so discovery will see updated name and location
+ nhcAction.setName(device.name);
+ nhcAction.setLocation(location);
+ } else {
+ logger.debug("adding action device {} model {}, {}", device.uuid, device.model, device.name);
+ nhcAction = new NhcAction2(device.uuid, device.name, device.type, device.technology, device.model,
+ location, actionType, this);
}
+ actions.put(device.uuid, nhcAction);
} else if ("thermostat".equals(device.type)) {
- if (!thermostats.containsKey(device.uuid)) {
- logger.debug("adding thermostat device {}, {}", device.uuid, device.name);
-
- NhcThermostat2 nhcThermostat = new NhcThermostat2(device.uuid, device.name, device.model,
- device.technology, location, this);
- thermostats.put(device.uuid, nhcThermostat);
+ NhcThermostat nhcThermostat = thermostats.get(device.uuid);
+ if (nhcThermostat != null) {
+ nhcThermostat.setName(device.name);
+ nhcThermostat.setLocation(location);
+ } else {
+ logger.debug("adding thermostat device {} model {}, {}", device.uuid, device.model, device.name);
+ nhcThermostat = new NhcThermostat2(device.uuid, device.name, device.type, device.technology,
+ device.model, location, this);
}
- } else if ("centralmeter".equals(device.type)) {
- if (!energyMeters.containsKey(device.uuid)) {
- logger.debug("adding centralmeter device {}, {}", device.uuid, device.name);
- NhcEnergyMeter2 nhcEnergyMeter = new NhcEnergyMeter2(device.uuid, device.name, device.model,
- device.technology, this, scheduler);
- energyMeters.put(device.uuid, nhcEnergyMeter);
+ thermostats.put(device.uuid, nhcThermostat);
+ } else if ("centralmeter".equals(device.type) || "energyhome".equals(device.type)) {
+ NhcEnergyMeter nhcEnergyMeter = energyMeters.get(device.uuid);
+ if (nhcEnergyMeter != null) {
+ nhcEnergyMeter.setName(device.name);
+ nhcEnergyMeter.setLocation(location);
+ } else {
+ logger.debug("adding energy meter device {} model {}, {}", device.uuid, device.model, device.name);
+ nhcEnergyMeter = new NhcEnergyMeter2(device.uuid, device.name, device.type, device.technology,
+ device.model, location, this, scheduler);
}
+ energyMeters.put(device.uuid, nhcEnergyMeter);
} else {
- logger.debug("device type {} not supported for {}, {}", device.type, device.uuid, device.name);
+ logger.debug("device type {} and model {} not supported for {}, {}", device.type, device.model, device.uuid,
+ device.name);
}
}
}
private void updateEnergyMeterState(NhcEnergyMeter2 energyMeter, List<NhcProperty> deviceProperties) {
- deviceProperties.stream().map(p -> p.electricalPower).filter(Objects::nonNull).findFirst()
- .ifPresent(electricalPower -> {
- try {
- // Sometimes API sends a fractional part, although API should only send whole units in W,
- // therefore drop fractional part
- energyMeter.setPower((int) Double.parseDouble(electricalPower));
- logger.trace("setting energy meter {} power to {}", energyMeter.getId(), electricalPower);
- } catch (NumberFormatException e) {
- energyMeter.setPower(null);
- logger.trace("received empty energy meter {} power reading", energyMeter.getId());
- }
- });
+ try {
+ Optional<Integer> electricalPower = deviceProperties.stream().map(p -> p.electricalPower)
+ .map(s -> (!((s == null) || s.isEmpty())) ? Math.round(Float.parseFloat(s)) : null)
+ .filter(Objects::nonNull).findFirst();
+ Optional<Integer> powerFromGrid = deviceProperties.stream().map(p -> p.electricalPowerFromGrid)
+ .map(s -> (!((s == null) || s.isEmpty())) ? Math.round(Float.parseFloat(s)) : null)
+ .filter(Objects::nonNull).findFirst();
+ Optional<Integer> powerToGrid = deviceProperties.stream().map(p -> p.electricalPowerToGrid)
+ .map(s -> (!((s == null) || s.isEmpty())) ? Math.round(Float.parseFloat(s)) : null)
+ .filter(Objects::nonNull).findFirst();
+ int power = electricalPower.orElse(powerFromGrid.orElse(0) - powerToGrid.orElse(0));
+ logger.trace("setting energy meter {} power to {}", energyMeter.getId(), electricalPower);
+ energyMeter.setPower(power);
+ } catch (NumberFormatException e) {
+ energyMeter.setPower(null);
+ logger.trace("received empty energy meter {} power reading", energyMeter.getId());
+ }
}
@Override
@Override
public void connectionStateChanged(MqttConnectionState state, @Nullable Throwable error) {
if (error != null) {
- logger.debug("Connection state: {}", state, error);
+ logger.debug("Connection state: {}, error", state, error);
String message = error.getLocalizedMessage();
message = (message != null) ? message : "@text/offline.communication-error";
if (!MqttConnectionState.CONNECTING.equals(state)) {