[nikohomecontrol] Add shutter invert and fix connection with NHC does not recover (#10281)

* Ignore devices.changed event.

Signed-off-by: Mark Herwege <mark.herwege@telenet.be>

* Null warnings cleanup.

Signed-off-by: Mark Herwege <mark.herwege@telenet.be>

* Remove @NonNullByDefault({})

Signed-off-by: Mark Herwege <mark.herwege@telenet.be>

* Shorten logger messages.

Signed-off-by: Mark Herwege <mark.herwege@telenet.be>

* Introduce rollershutter invert flag.

Signed-off-by: Mark Herwege <mark.herwege@telenet.be>

* Add null annotations.

Signed-off-by: Mark Herwege <mark.herwege@telenet.be>

* Connection resilience improvements and log level cleanup.

Signed-off-by: Mark Herwege <mark.herwege@telenet.be>

* Update bundles/org.openhab.binding.nikohomecontrol/src/main/resources/OH-INF/thing/thing-types.xml

Signed-off-by: Fabian Wolter <github@fabian-wolter.de>

Co-authored-by: Fabian Wolter <github@fabian-wolter.de>
This commit is contained in:
Mark Herwege
2021-03-13 20:44:36 +01:00
committed by GitHub
parent affc082fb5
commit 286bced20e
38 changed files with 456 additions and 262 deletions

View File

@@ -55,7 +55,7 @@ public class NikoHomeControlBridgeDiscoveryService extends AbstractDiscoveryServ
public NikoHomeControlBridgeDiscoveryService() { public NikoHomeControlBridgeDiscoveryService() {
super(NikoHomeControlBindingConstants.BRIDGE_THING_TYPES_UIDS, TIMEOUT); super(NikoHomeControlBindingConstants.BRIDGE_THING_TYPES_UIDS, TIMEOUT);
logger.debug("Niko Home Control: bridge discovery service started"); logger.debug("bridge discovery service started");
} }
/** /**
@@ -65,10 +65,10 @@ public class NikoHomeControlBridgeDiscoveryService extends AbstractDiscoveryServ
try { try {
String broadcastAddr = networkAddressService.getConfiguredBroadcastAddress(); String broadcastAddr = networkAddressService.getConfiguredBroadcastAddress();
if (broadcastAddr == null) { if (broadcastAddr == null) {
logger.warn("Niko Home Control: discovery not possible, no broadcast address found"); logger.warn("discovery not possible, no broadcast address found");
return; return;
} }
logger.debug("Niko Home Control: discovery broadcast on {}", broadcastAddr); logger.debug("discovery broadcast on {}", broadcastAddr);
NikoHomeControlDiscover nhcDiscover = new NikoHomeControlDiscover(broadcastAddr); NikoHomeControlDiscover nhcDiscover = new NikoHomeControlDiscover(broadcastAddr);
if (nhcDiscover.isNhcII()) { if (nhcDiscover.isNhcII()) {
addNhcIIBridge(nhcDiscover.getAddr(), nhcDiscover.getNhcBridgeId()); addNhcIIBridge(nhcDiscover.getAddr(), nhcDiscover.getNhcBridgeId());
@@ -76,12 +76,12 @@ public class NikoHomeControlBridgeDiscoveryService extends AbstractDiscoveryServ
addNhcIBridge(nhcDiscover.getAddr(), nhcDiscover.getNhcBridgeId()); addNhcIBridge(nhcDiscover.getAddr(), nhcDiscover.getNhcBridgeId());
} }
} catch (IOException e) { } catch (IOException e) {
logger.debug("Niko Home Control: no bridge found."); logger.debug("no bridge found.");
} }
} }
private void addNhcIBridge(InetAddress addr, String bridgeId) { private void addNhcIBridge(InetAddress addr, String bridgeId) {
logger.debug("Niko Home Control: NHC I bridge found at {}", addr); logger.debug("NHC I bridge found at {}", addr);
String bridgeName = "Niko Home Control Bridge"; String bridgeName = "Niko Home Control Bridge";
ThingUID uid = new ThingUID(BINDING_ID, "bridge", bridgeId); ThingUID uid = new ThingUID(BINDING_ID, "bridge", bridgeId);
@@ -92,7 +92,7 @@ public class NikoHomeControlBridgeDiscoveryService extends AbstractDiscoveryServ
} }
private void addNhcIIBridge(InetAddress addr, String bridgeId) { private void addNhcIIBridge(InetAddress addr, String bridgeId) {
logger.debug("Niko Home Control: NHC II bridge found at {}", addr); logger.debug("NHC II bridge found at {}", addr);
String bridgeName = "Niko Home Control II Bridge"; String bridgeName = "Niko Home Control II Bridge";
ThingUID uid = new ThingUID(BINDING_ID, "bridge2", bridgeId); ThingUID uid = new ThingUID(BINDING_ID, "bridge2", bridgeId);
@@ -115,7 +115,7 @@ public class NikoHomeControlBridgeDiscoveryService extends AbstractDiscoveryServ
@Override @Override
protected void startBackgroundDiscovery() { protected void startBackgroundDiscovery() {
logger.debug("Niko Home Control: Start background bridge discovery"); logger.debug("Start background bridge discovery");
ScheduledFuture<?> job = nhcDiscoveryJob; ScheduledFuture<?> job = nhcDiscoveryJob;
if (job == null || job.isCancelled()) { if (job == null || job.isCancelled()) {
nhcDiscoveryJob = scheduler.scheduleWithFixedDelay(this::discoverBridge, 0, REFRESH_INTERVAL, nhcDiscoveryJob = scheduler.scheduleWithFixedDelay(this::discoverBridge, 0, REFRESH_INTERVAL,
@@ -125,7 +125,7 @@ public class NikoHomeControlBridgeDiscoveryService extends AbstractDiscoveryServ
@Override @Override
protected void stopBackgroundDiscovery() { protected void stopBackgroundDiscovery() {
logger.debug("Niko Home Control: Stop bridge background discovery"); logger.debug("Stop bridge background discovery");
ScheduledFuture<?> job = nhcDiscoveryJob; ScheduledFuture<?> job = nhcDiscoveryJob;
if (job != null && !job.isCancelled()) { if (job != null && !job.isCancelled()) {
job.cancel(true); job.cancel(true);

View File

@@ -48,7 +48,7 @@ public class NikoHomeControlDiscoveryService extends AbstractDiscoveryService {
public NikoHomeControlDiscoveryService(NikoHomeControlBridgeHandler handler) { public NikoHomeControlDiscoveryService(NikoHomeControlBridgeHandler handler) {
super(SUPPORTED_THING_TYPES_UIDS, TIMEOUT, false); super(SUPPORTED_THING_TYPES_UIDS, TIMEOUT, false);
logger.debug("Niko Home Control: discovery service {}", handler); logger.debug("discovery service {}", handler);
bridgeUID = handler.getThing().getUID(); bridgeUID = handler.getThing().getUID();
this.handler = handler; this.handler = handler;
} }
@@ -70,10 +70,10 @@ public class NikoHomeControlDiscoveryService extends AbstractDiscoveryService {
NikoHomeControlCommunication nhcComm = handler.getCommunication(); NikoHomeControlCommunication nhcComm = handler.getCommunication();
if ((nhcComm == null) || !nhcComm.communicationActive()) { if ((nhcComm == null) || !nhcComm.communicationActive()) {
logger.warn("Niko Home Control: not connected."); logger.warn("not connected");
return; return;
} }
logger.debug("Niko Home Control: getting devices on {}", handler.getThing().getUID().getId()); logger.debug("getting devices on {}", handler.getThing().getUID().getId());
Map<String, NhcAction> actions = nhcComm.getActions(); Map<String, NhcAction> actions = nhcComm.getActions();
@@ -99,8 +99,7 @@ public class NikoHomeControlDiscoveryService extends AbstractDiscoveryService {
thingName, thingLocation); thingName, thingLocation);
break; break;
default: default:
logger.debug("Niko Home Control: unrecognized action type {} for {} {}", nhcAction.getType(), logger.debug("unrecognized action type {} for {} {}", nhcAction.getType(), actionId, thingName);
actionId, thingName);
} }
}); });

View File

@@ -14,17 +14,20 @@ package org.openhab.binding.nikohomecontrol.internal.handler;
import java.util.List; import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
/** /**
* {@link NhcJwtToken2} represents the Niko Home Control II hobby API token payload. * {@link NhcJwtToken2} represents the Niko Home Control II hobby API token payload.
* *
* @author Mark Herwege - Initial Contribution * @author Mark Herwege - Initial Contribution
*/ */
@NonNullByDefault
class NhcJwtToken2 { class NhcJwtToken2 {
String sub; String sub = "";
String iat; String iat = "";
String exp; String exp = "";
String aud; String aud = "";
String iss; String iss = "";
String jti; String jti = "";
List<String> role; List<String> role = List.of();
} }

View File

@@ -0,0 +1,25 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.nikohomecontrol.internal.handler;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* {@link NikoHomeControlActionBlindConfig} is the config class for Niko Home Control Blind Actions.
*
* @author Mark Herwege - Initial Contribution
*/
@NonNullByDefault
public class NikoHomeControlActionBlindConfig extends NikoHomeControlActionConfig {
public boolean invert;
}

View File

@@ -12,11 +12,14 @@
*/ */
package org.openhab.binding.nikohomecontrol.internal.handler; package org.openhab.binding.nikohomecontrol.internal.handler;
import org.eclipse.jdt.annotation.NonNullByDefault;
/** /**
* {@link NikoHomeControlActionConfig} is the general config class for Niko Home Control Actions. * {@link NikoHomeControlActionConfig} is the general config class for Niko Home Control Actions.
* *
* @author Mark Herwege - Initial Contribution * @author Mark Herwege - Initial Contribution
*/ */
@NonNullByDefault
public class NikoHomeControlActionConfig { public class NikoHomeControlActionConfig {
public String actionId; public String actionId = "";
} }

View File

@@ -12,11 +12,14 @@
*/ */
package org.openhab.binding.nikohomecontrol.internal.handler; package org.openhab.binding.nikohomecontrol.internal.handler;
import org.eclipse.jdt.annotation.NonNullByDefault;
/** /**
* {@link NikoHomeControlActionDimmerConfig} is the config class for Niko Home Control Dimmer Actions. * {@link NikoHomeControlActionDimmerConfig} is the config class for Niko Home Control Dimmer Actions.
* *
* @author Mark Herwege - Initial Contribution * @author Mark Herwege - Initial Contribution
*/ */
@NonNullByDefault
public class NikoHomeControlActionDimmerConfig extends NikoHomeControlActionConfig { public class NikoHomeControlActionDimmerConfig extends NikoHomeControlActionConfig {
public int step; public int step = 10;
} }

View File

@@ -52,10 +52,11 @@ public class NikoHomeControlActionHandler extends BaseThingHandler implements Nh
private final Logger logger = LoggerFactory.getLogger(NikoHomeControlActionHandler.class); private final Logger logger = LoggerFactory.getLogger(NikoHomeControlActionHandler.class);
private volatile @NonNullByDefault({}) NhcAction nhcAction; private volatile @Nullable NhcAction nhcAction;
private String actionId = ""; private String actionId = "";
private int stepValue; private int stepValue;
private boolean invert;
public NikoHomeControlActionHandler(Thing thing) { public NikoHomeControlActionHandler(Thing thing) {
super(thing); super(thing);
@@ -66,8 +67,7 @@ public class NikoHomeControlActionHandler extends BaseThingHandler implements Nh
NikoHomeControlCommunication nhcComm = getCommunication(); NikoHomeControlCommunication nhcComm = getCommunication();
if (nhcComm == null) { if (nhcComm == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"Niko Home Control: bridge communication not initialized when trying to execute action command " "Bridge communication not initialized when trying to execute action command " + actionId);
+ actionId);
return; return;
} }
@@ -84,7 +84,13 @@ public class NikoHomeControlActionHandler extends BaseThingHandler implements Nh
} }
private void handleCommandSelection(ChannelUID channelUID, Command command) { private void handleCommandSelection(ChannelUID channelUID, Command command) {
logger.debug("Niko Home Control: handle command {} for {}", command, channelUID); NhcAction nhcAction = this.nhcAction;
if (nhcAction == null) {
logger.debug("action with ID {} not initialized", actionId);
return;
}
logger.debug("handle command {} for {}", command, channelUID);
if (command == REFRESH) { if (command == REFRESH) {
actionEvent(nhcAction.getState()); actionEvent(nhcAction.getState());
@@ -109,11 +115,17 @@ public class NikoHomeControlActionHandler extends BaseThingHandler implements Nh
default: default:
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"Niko Home Control: channel unknown " + channelUID.getId()); "Channel unknown " + channelUID.getId());
} }
} }
private void handleSwitchCommand(Command command) { private void handleSwitchCommand(Command command) {
NhcAction nhcAction = this.nhcAction;
if (nhcAction == null) {
logger.debug("action with ID {} not initialized", actionId);
return;
}
if (command instanceof OnOffType) { if (command instanceof OnOffType) {
OnOffType s = (OnOffType) command; OnOffType s = (OnOffType) command;
if (OnOffType.OFF.equals(s)) { if (OnOffType.OFF.equals(s)) {
@@ -125,6 +137,12 @@ public class NikoHomeControlActionHandler extends BaseThingHandler implements Nh
} }
private void handleBrightnessCommand(Command command) { private void handleBrightnessCommand(Command command) {
NhcAction nhcAction = this.nhcAction;
if (nhcAction == null) {
logger.debug("action with ID {} not initialized", actionId);
return;
}
if (command instanceof OnOffType) { if (command instanceof OnOffType) {
OnOffType s = (OnOffType) command; OnOffType s = (OnOffType) command;
if (OnOffType.OFF.equals(s)) { if (OnOffType.OFF.equals(s)) {
@@ -162,18 +180,24 @@ public class NikoHomeControlActionHandler extends BaseThingHandler implements Nh
} }
private void handleRollershutterCommand(Command command) { private void handleRollershutterCommand(Command command) {
NhcAction nhcAction = this.nhcAction;
if (nhcAction == null) {
logger.debug("action with ID {} not initialized", actionId);
return;
}
if (command instanceof UpDownType) { if (command instanceof UpDownType) {
UpDownType s = (UpDownType) command; UpDownType s = (UpDownType) command;
if (UpDownType.UP.equals(s)) { if (UpDownType.UP.equals(s)) {
nhcAction.execute(NHCUP); nhcAction.execute(!invert ? NHCUP : NHCDOWN);
} else { } else {
nhcAction.execute(NHCDOWN); nhcAction.execute(!invert ? NHCDOWN : NHCUP);
} }
} else if (command instanceof StopMoveType) { } else if (command instanceof StopMoveType) {
nhcAction.execute(NHCSTOP); nhcAction.execute(NHCSTOP);
} else if (command instanceof PercentType) { } else if (command instanceof PercentType) {
PercentType p = (PercentType) command; PercentType p = (PercentType) command;
nhcAction.execute(Integer.toString(100 - p.intValue())); nhcAction.execute(!invert ? Integer.toString(100 - p.intValue()) : Integer.toString(p.intValue()));
} }
} }
@@ -183,6 +207,9 @@ public class NikoHomeControlActionHandler extends BaseThingHandler implements Nh
if (thing.getThingTypeUID().equals(THING_TYPE_DIMMABLE_LIGHT)) { if (thing.getThingTypeUID().equals(THING_TYPE_DIMMABLE_LIGHT)) {
config = getConfig().as(NikoHomeControlActionDimmerConfig.class); config = getConfig().as(NikoHomeControlActionDimmerConfig.class);
stepValue = ((NikoHomeControlActionDimmerConfig) config).step; stepValue = ((NikoHomeControlActionDimmerConfig) config).step;
} else if (thing.getThingTypeUID().equals(THING_TYPE_BLIND)) {
config = getConfig().as(NikoHomeControlActionBlindConfig.class);
invert = ((NikoHomeControlActionBlindConfig) config).invert;
} else { } else {
config = getConfig().as(NikoHomeControlActionConfig.class); config = getConfig().as(NikoHomeControlActionConfig.class);
} }
@@ -190,6 +217,8 @@ public class NikoHomeControlActionHandler extends BaseThingHandler implements Nh
NikoHomeControlCommunication nhcComm = getCommunication(); NikoHomeControlCommunication nhcComm = getCommunication();
if (nhcComm == null) { if (nhcComm == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"Connection with controller not started yet, could not initialize action " + actionId);
return; return;
} }
@@ -197,15 +226,14 @@ public class NikoHomeControlActionHandler extends BaseThingHandler implements Nh
scheduler.submit(() -> { scheduler.submit(() -> {
if (!nhcComm.communicationActive()) { if (!nhcComm.communicationActive()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"Niko Home Control: no connection with Niko Home Control, could not initialize action " "No connection with controller, could not initialize action " + actionId);
+ actionId);
return; return;
} }
nhcAction = nhcComm.getActions().get(actionId); NhcAction nhcAction = nhcComm.getActions().get(actionId);
if (nhcAction == null) { if (nhcAction == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"Niko Home Control: actionId does not match an action in the controller " + actionId); "Action " + actionId + " does not match an action in the controller");
return; return;
} }
@@ -220,7 +248,9 @@ public class NikoHomeControlActionHandler extends BaseThingHandler implements Nh
actionEvent(nhcAction.getState()); actionEvent(nhcAction.getState());
logger.debug("Niko Home Control: action initialized {}", actionId); this.nhcAction = nhcAction;
logger.debug("action initialized {}", actionId);
Bridge bridge = getBridge(); Bridge bridge = getBridge();
if ((bridge != null) && (bridge.getStatus() == ThingStatus.ONLINE)) { if ((bridge != null) && (bridge.getStatus() == ThingStatus.ONLINE)) {
@@ -232,6 +262,12 @@ public class NikoHomeControlActionHandler extends BaseThingHandler implements Nh
} }
private void updateProperties() { private void updateProperties() {
NhcAction nhcAction = this.nhcAction;
if (nhcAction == null) {
logger.debug("action with ID {} not initialized", actionId);
return;
}
Map<String, String> properties = new HashMap<>(); Map<String, String> properties = new HashMap<>();
properties.put("type", String.valueOf(nhcAction.getType())); properties.put("type", String.valueOf(nhcAction.getType()));
if (getThing().getThingTypeUID() == THING_TYPE_BLIND) { if (getThing().getThingTypeUID() == THING_TYPE_BLIND) {
@@ -250,6 +286,12 @@ public class NikoHomeControlActionHandler extends BaseThingHandler implements Nh
@Override @Override
public void actionEvent(int actionState) { public void actionEvent(int actionState) {
NhcAction nhcAction = this.nhcAction;
if (nhcAction == null) {
logger.debug("action with ID {} not initialized", actionId);
return;
}
ActionType actionType = nhcAction.getType(); ActionType actionType = nhcAction.getType();
switch (actionType) { switch (actionType) {
@@ -265,19 +307,28 @@ public class NikoHomeControlActionHandler extends BaseThingHandler implements Nh
updateStatus(ThingStatus.ONLINE); updateStatus(ThingStatus.ONLINE);
break; break;
case ROLLERSHUTTER: case ROLLERSHUTTER:
updateState(CHANNEL_ROLLERSHUTTER, new PercentType(actionState)); updateState(CHANNEL_ROLLERSHUTTER,
!invert ? new PercentType(100 - actionState) : new PercentType(actionState));
updateStatus(ThingStatus.ONLINE); updateStatus(ThingStatus.ONLINE);
break; break;
default: default:
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"Niko Home Control: unknown action type " + actionType); "Unknown action type " + actionType);
}
}
@Override
public void actionInitialized() {
Bridge bridge = getBridge();
if ((bridge != null) && (bridge.getStatus() == ThingStatus.ONLINE)) {
updateStatus(ThingStatus.ONLINE);
} }
} }
@Override @Override
public void actionRemoved() { public void actionRemoved() {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"Niko Home Control: action has been removed from the controller " + actionId); "Action " + actionId + " has been removed from the controller");
} }
private void restartCommunication(NikoHomeControlCommunication nhcComm) { private void restartCommunication(NikoHomeControlCommunication nhcComm) {
@@ -286,8 +337,7 @@ public class NikoHomeControlActionHandler extends BaseThingHandler implements Nh
nhcComm.restartCommunication(); nhcComm.restartCommunication();
// If still not active, take thing offline and return. // If still not active, take thing offline and return.
if (!nhcComm.communicationActive()) { if (!nhcComm.communicationActive()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Communication error");
"Niko Home Control: communication socket error");
return; return;
} }
// Also put the bridge back online // Also put the bridge back online
@@ -301,7 +351,7 @@ public class NikoHomeControlActionHandler extends BaseThingHandler implements Nh
NikoHomeControlBridgeHandler nhcBridgeHandler = getBridgeHandler(); NikoHomeControlBridgeHandler nhcBridgeHandler = getBridgeHandler();
if (nhcBridgeHandler == null) { if (nhcBridgeHandler == null) {
updateStatus(ThingStatus.UNINITIALIZED, ThingStatusDetail.BRIDGE_UNINITIALIZED, updateStatus(ThingStatus.UNINITIALIZED, ThingStatusDetail.BRIDGE_UNINITIALIZED,
"Niko Home Control: no bridge initialized for action " + actionId); "No bridge initialized for action " + actionId);
return null; return null;
} }
NikoHomeControlCommunication nhcComm = nhcBridgeHandler.getCommunication(); NikoHomeControlCommunication nhcComm = nhcBridgeHandler.getCommunication();
@@ -312,7 +362,7 @@ public class NikoHomeControlActionHandler extends BaseThingHandler implements Nh
Bridge nhcBridge = getBridge(); Bridge nhcBridge = getBridge();
if (nhcBridge == null) { if (nhcBridge == null) {
updateStatus(ThingStatus.UNINITIALIZED, ThingStatusDetail.BRIDGE_UNINITIALIZED, updateStatus(ThingStatus.UNINITIALIZED, ThingStatusDetail.BRIDGE_UNINITIALIZED,
"Niko Home Control: no bridge initialized for action " + actionId); "No bridge initialized for action " + actionId);
return null; return null;
} }
NikoHomeControlBridgeHandler nhcBridgeHandler = (NikoHomeControlBridgeHandler) nhcBridge.getHandler(); NikoHomeControlBridgeHandler nhcBridgeHandler = (NikoHomeControlBridgeHandler) nhcBridge.getHandler();

View File

@@ -12,13 +12,16 @@
*/ */
package org.openhab.binding.nikohomecontrol.internal.handler; package org.openhab.binding.nikohomecontrol.internal.handler;
import org.eclipse.jdt.annotation.NonNullByDefault;
/** /**
* {@link NikoHomeControlBridgeConfig} is the general config class for Niko Home Control Bridges. * {@link NikoHomeControlBridgeConfig} is the general config class for Niko Home Control Bridges.
* *
* @author Mark Herwege - Initial Contribution * @author Mark Herwege - Initial Contribution
*/ */
@NonNullByDefault
public class NikoHomeControlBridgeConfig { public class NikoHomeControlBridgeConfig {
public String addr; public String addr = "";
public int port; public int port;
public int refresh; public int refresh;
} }

View File

@@ -12,12 +12,15 @@
*/ */
package org.openhab.binding.nikohomecontrol.internal.handler; package org.openhab.binding.nikohomecontrol.internal.handler;
import org.eclipse.jdt.annotation.NonNullByDefault;
/** /**
* {@link NikoHomeControlBridgeConfig2} is the extended config class for Niko Home Control II Bridges. * {@link NikoHomeControlBridgeConfig2} is the extended config class for Niko Home Control II Bridges.
* *
* @author Mark Herwege - Initial Contribution * @author Mark Herwege - Initial Contribution
*/ */
@NonNullByDefault
public class NikoHomeControlBridgeConfig2 extends NikoHomeControlBridgeConfig { public class NikoHomeControlBridgeConfig2 extends NikoHomeControlBridgeConfig {
public String profile; public String profile = "";
public String password; public String password = "";
} }

View File

@@ -95,7 +95,7 @@ public abstract class NikoHomeControlBridgeHandler extends BaseBridgeHandler imp
if (discovery != null) { if (discovery != null) {
discovery.discoverDevices(); discovery.discoverDevices();
} else { } else {
logger.debug("Niko Home Control: cannot discover devices, discovery service not started"); logger.debug("cannot discover devices, discovery service not started");
} }
}); });
} }
@@ -117,9 +117,9 @@ public abstract class NikoHomeControlBridgeHandler extends BaseBridgeHandler imp
} }
// This timer will restart the bridge connection periodically // This timer will restart the bridge connection periodically
logger.debug("Niko Home Control: restart bridge connection every {} min", refreshInterval); logger.debug("restart bridge connection every {} min", refreshInterval);
refreshTimer = scheduler.scheduleWithFixedDelay(() -> { refreshTimer = scheduler.scheduleWithFixedDelay(() -> {
logger.debug("Niko Home Control: restart communication at scheduled time"); logger.debug("restart communication at scheduled time");
NikoHomeControlCommunication comm = nhcComm; NikoHomeControlCommunication comm = nhcComm;
if (comm != null) { if (comm != null) {
@@ -141,7 +141,7 @@ public abstract class NikoHomeControlBridgeHandler extends BaseBridgeHandler imp
*/ */
protected void bridgeOffline() { protected void bridgeOffline() {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
"Niko Home Control: error starting bridge connection"); "Error with bridge connection");
} }
/** /**
@@ -153,8 +153,8 @@ public abstract class NikoHomeControlBridgeHandler extends BaseBridgeHandler imp
} }
@Override @Override
public void controllerOffline() { public void controllerOffline(String message) {
bridgeOffline(); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, message);
} }
@Override @Override
@@ -231,14 +231,14 @@ public abstract class NikoHomeControlBridgeHandler extends BaseBridgeHandler imp
@Override @Override
public void alarmEvent(String alarmText) { public void alarmEvent(String alarmText) {
logger.debug("Niko Home Control: triggering alarm channel with {}", alarmText); logger.debug("triggering alarm channel with {}", alarmText);
triggerChannel(CHANNEL_ALARM, alarmText); triggerChannel(CHANNEL_ALARM, alarmText);
updateStatus(ThingStatus.ONLINE); updateStatus(ThingStatus.ONLINE);
} }
@Override @Override
public void noticeEvent(String alarmText) { public void noticeEvent(String alarmText) {
logger.debug("Niko Home Control: triggering notice channel with {}", alarmText); logger.debug("triggering notice channel with {}", alarmText);
triggerChannel(CHANNEL_NOTICE, alarmText); triggerChannel(CHANNEL_NOTICE, alarmText);
updateStatus(ThingStatus.ONLINE); updateStatus(ThingStatus.ONLINE);
} }
@@ -263,7 +263,7 @@ public abstract class NikoHomeControlBridgeHandler extends BaseBridgeHandler imp
try { try {
addr = InetAddress.getByName(config.addr); addr = InetAddress.getByName(config.addr);
} catch (UnknownHostException e) { } catch (UnknownHostException e) {
logger.debug("Niko Home Control: Cannot resolve hostname {} to IP adress", config.addr); logger.debug("Cannot resolve hostname {} to IP adress", config.addr);
} }
return addr; return addr;
} }

View File

@@ -41,20 +41,20 @@ public class NikoHomeControlBridgeHandler1 extends NikoHomeControlBridgeHandler
@Override @Override
public void initialize() { public void initialize() {
logger.debug("Niko Home Control: initializing bridge handler"); logger.debug("initializing bridge handler");
setConfig(); setConfig();
InetAddress addr = getAddr(); InetAddress addr = getAddr();
int port = getPort(); int port = getPort();
logger.debug("Niko Home Control: bridge handler host {}, port {}", addr, port); logger.debug("bridge handler host {}, port {}", addr, port);
if (addr != null) { if (addr != null) {
nhcComm = new NikoHomeControlCommunication1(this, scheduler); nhcComm = new NikoHomeControlCommunication1(this, scheduler);
startCommunication(); startCommunication();
} else { } else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
"Niko Home Control: cannot resolve bridge IP with hostname " + config.addr); "Cannot resolve bridge IP with hostname " + config.addr);
} }
} }

View File

@@ -57,7 +57,7 @@ public class NikoHomeControlBridgeHandler2 extends NikoHomeControlBridgeHandler
@Override @Override
public void initialize() { public void initialize() {
logger.debug("Niko Home Control: initializing NHC II bridge handler"); logger.debug("initializing NHC II bridge handler");
setConfig(); setConfig();
@@ -69,15 +69,14 @@ public class NikoHomeControlBridgeHandler2 extends NikoHomeControlBridgeHandler
// advanced configuration, skipping token validation. // advanced configuration, skipping token validation.
// This behavior would allow the same logic to be used (with profile UUID) as before token validation // This behavior would allow the same logic to be used (with profile UUID) as before token validation
// was introduced. // was introduced.
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, "Token is empty");
"Niko Home Control: token is empty");
return; return;
} }
} else { } else {
Date now = new Date(); Date now = new Date();
if (expiryDate.before(now)) { if (expiryDate.before(now)) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR,
"Niko Home Control: hobby api token has expired"); "Hobby api token has expired");
return; return;
} }
} }
@@ -91,7 +90,7 @@ public class NikoHomeControlBridgeHandler2 extends NikoHomeControlBridgeHandler
} catch (CertificateException e) { } catch (CertificateException e) {
// this should not happen unless there is a programming error // this should not happen unless there is a programming error
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
"Niko Home Control: not able to set SSL context"); "Not able to set SSL context");
return; return;
} }
} }
@@ -167,9 +166,8 @@ public class NikoHomeControlBridgeHandler2 extends NikoHomeControlBridgeHandler
@Override @Override
public String getToken() { public String getToken() {
String token = ((NikoHomeControlBridgeConfig2) config).password; String token = ((NikoHomeControlBridgeConfig2) config).password;
if ((token == null) || token.isEmpty()) { if (token.isEmpty()) {
logger.debug("Niko Home Control: no JWT token set."); logger.debug("no JWT token set.");
return "";
} }
return token; return token;
} }
@@ -192,10 +190,10 @@ public class NikoHomeControlBridgeHandler2 extends NikoHomeControlBridgeHandler
try { try {
jwtToken = gson.fromJson(tokenPayload, NhcJwtToken2.class); jwtToken = gson.fromJson(tokenPayload, NhcJwtToken2.class);
} catch (JsonSyntaxException e) { } catch (JsonSyntaxException e) {
logger.debug("Niko Home Control: unexpected token payload {}", tokenPayload); logger.debug("unexpected token payload {}", tokenPayload);
} catch (NoSuchElementException ignore) { } catch (NoSuchElementException ignore) {
// Ignore if exp not present in response, this should not happen in token payload response // Ignore if exp not present in response, this should not happen in token payload response
logger.trace("Niko Home Control: no expiry date found in payload {}", tokenPayload); logger.trace("no expiry date found in payload {}", tokenPayload);
} }
} }
@@ -206,20 +204,20 @@ public class NikoHomeControlBridgeHandler2 extends NikoHomeControlBridgeHandler
long epoch = Long.parseLong(expiryEpoch) * 1000; // convert to milliseconds long epoch = Long.parseLong(expiryEpoch) * 1000; // convert to milliseconds
expiryDate = new Date(epoch); expiryDate = new Date(epoch);
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
logger.debug("Niko Home Control: token expiry not valid {}", jwtToken.exp); logger.debug("token expiry not valid {}", jwtToken.exp);
return null; return null;
} }
Date now = new Date(); Date now = new Date();
if (expiryDate.before(now)) { if (expiryDate.before(now)) {
logger.warn("Niko Home Control: hobby API token expired, was valid until {}", logger.warn("hobby API token expired, was valid until {}",
DateFormat.getDateInstance().format(expiryDate)); DateFormat.getDateInstance().format(expiryDate));
} else { } else {
Calendar c = Calendar.getInstance(); Calendar c = Calendar.getInstance();
c.setTime(expiryDate); c.setTime(expiryDate);
c.add(Calendar.DATE, -14); c.add(Calendar.DATE, -14);
if (c.getTime().before(now)) { if (c.getTime().before(now)) {
logger.info("Niko Home Control: hobby API token will expire in less than 14 days, valid until {}", logger.info("hobby API token will expire in less than 14 days, valid until {}",
DateFormat.getDateInstance().format(expiryDate)); DateFormat.getDateInstance().format(expiryDate));
} }
} }

View File

@@ -12,11 +12,14 @@
*/ */
package org.openhab.binding.nikohomecontrol.internal.handler; package org.openhab.binding.nikohomecontrol.internal.handler;
import org.eclipse.jdt.annotation.NonNullByDefault;
/** /**
* {@link NikoHomeControlEnergyMeterConfig} is the config class for Niko Home Control Thermostats. * {@link NikoHomeControlEnergyMeterConfig} is the config class for Niko Home Control Thermostats.
* *
* @author Mark Herwege - Initial Contribution * @author Mark Herwege - Initial Contribution
*/ */
@NonNullByDefault
public class NikoHomeControlEnergyMeterConfig { public class NikoHomeControlEnergyMeterConfig {
public String energyMeterId; public String energyMeterId = "";
} }

View File

@@ -48,7 +48,7 @@ public class NikoHomeControlEnergyMeterHandler extends BaseThingHandler implemen
private final Logger logger = LoggerFactory.getLogger(NikoHomeControlEnergyMeterHandler.class); private final Logger logger = LoggerFactory.getLogger(NikoHomeControlEnergyMeterHandler.class);
private volatile @NonNullByDefault({}) NhcEnergyMeter nhcEnergyMeter; private volatile @Nullable NhcEnergyMeter nhcEnergyMeter;
private String energyMeterId = ""; private String energyMeterId = "";
@@ -58,6 +58,12 @@ public class NikoHomeControlEnergyMeterHandler extends BaseThingHandler implemen
@Override @Override
public void handleCommand(ChannelUID channelUID, Command command) { public void handleCommand(ChannelUID channelUID, Command command) {
NhcEnergyMeter nhcEnergyMeter = this.nhcEnergyMeter;
if (nhcEnergyMeter == null) {
logger.debug("energy meter with ID {} not initialized", energyMeterId);
return;
}
if (command == REFRESH) { if (command == REFRESH) {
energyMeterEvent(nhcEnergyMeter.getPower()); energyMeterEvent(nhcEnergyMeter.getPower());
} }
@@ -71,6 +77,8 @@ public class NikoHomeControlEnergyMeterHandler extends BaseThingHandler implemen
NikoHomeControlCommunication nhcComm = getCommunication(); NikoHomeControlCommunication nhcComm = getCommunication();
if (nhcComm == null) { if (nhcComm == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"Connection with controller not started yet, could not initialize energy meter " + energyMeterId);
return; return;
} }
@@ -79,16 +87,14 @@ public class NikoHomeControlEnergyMeterHandler extends BaseThingHandler implemen
scheduler.submit(() -> { scheduler.submit(() -> {
if (!nhcComm.communicationActive()) { if (!nhcComm.communicationActive()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"Niko Home Control: no connection with Niko Home Control, could not initialize energy meter " "No connection with controller, could not initialize energy meter " + energyMeterId);
+ energyMeterId);
return; return;
} }
nhcEnergyMeter = nhcComm.getEnergyMeters().get(energyMeterId); NhcEnergyMeter nhcEnergyMeter = nhcComm.getEnergyMeters().get(energyMeterId);
if (nhcEnergyMeter == null) { if (nhcEnergyMeter == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"Niko Home Control: energyMeterId does not match a energy meter in the controller " "Energy meter " + energyMeterId + " does not match a energy meter in the controller");
+ energyMeterId);
return; return;
} }
@@ -102,7 +108,9 @@ public class NikoHomeControlEnergyMeterHandler extends BaseThingHandler implemen
nhcComm.startEnergyMeter(energyMeterId); nhcComm.startEnergyMeter(energyMeterId);
} }
logger.debug("Niko Home Control: energy meter intialized {}", energyMeterId); this.nhcEnergyMeter = nhcEnergyMeter;
logger.debug("energy meter intialized {}", energyMeterId);
Bridge bridge = getBridge(); Bridge bridge = getBridge();
if ((bridge != null) && (bridge.getStatus() == ThingStatus.ONLINE)) { if ((bridge != null) && (bridge.getStatus() == ThingStatus.ONLINE)) {
@@ -144,10 +152,18 @@ public class NikoHomeControlEnergyMeterHandler extends BaseThingHandler implemen
updateStatus(ThingStatus.ONLINE); updateStatus(ThingStatus.ONLINE);
} }
@Override
public void energyMeterInitialized() {
Bridge bridge = getBridge();
if ((bridge != null) && (bridge.getStatus() == ThingStatus.ONLINE)) {
updateStatus(ThingStatus.ONLINE);
}
}
@Override @Override
public void energyMeterRemoved() { public void energyMeterRemoved() {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"Niko Home Control: energy meter has been removed from the controller " + energyMeterId); "Energy meter " + energyMeterId + " has been removed from the controller");
} }
@Override @Override
@@ -157,8 +173,7 @@ public class NikoHomeControlEnergyMeterHandler extends BaseThingHandler implemen
NikoHomeControlCommunication nhcComm = getCommunication(); NikoHomeControlCommunication nhcComm = getCommunication();
if (nhcComm == null) { if (nhcComm == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"Niko Home Control: bridge communication not initialized when trying to start energy meter " "Bridge communication not initialized when trying to start energy meter " + energyMeterId);
+ energyMeterId);
return; return;
} }
@@ -180,8 +195,7 @@ public class NikoHomeControlEnergyMeterHandler extends BaseThingHandler implemen
NikoHomeControlCommunication nhcComm = getCommunication(); NikoHomeControlCommunication nhcComm = getCommunication();
if (nhcComm == null) { if (nhcComm == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"Niko Home Control: bridge communication not initialized when trying to stop energy meter " "Bridge communication not initialized when trying to stop energy meter " + energyMeterId);
+ energyMeterId);
return; return;
} }
@@ -206,8 +220,7 @@ public class NikoHomeControlEnergyMeterHandler extends BaseThingHandler implemen
nhcComm.restartCommunication(); nhcComm.restartCommunication();
// If still not active, take thing offline and return. // If still not active, take thing offline and return.
if (!nhcComm.communicationActive()) { if (!nhcComm.communicationActive()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Communication error");
"Niko Home Control: communication socket error");
return; return;
} }
// Also put the bridge back online // Also put the bridge back online
@@ -221,7 +234,7 @@ public class NikoHomeControlEnergyMeterHandler extends BaseThingHandler implemen
NikoHomeControlBridgeHandler nhcBridgeHandler = getBridgeHandler(); NikoHomeControlBridgeHandler nhcBridgeHandler = getBridgeHandler();
if (nhcBridgeHandler == null) { if (nhcBridgeHandler == null) {
updateStatus(ThingStatus.UNINITIALIZED, ThingStatusDetail.BRIDGE_UNINITIALIZED, updateStatus(ThingStatus.UNINITIALIZED, ThingStatusDetail.BRIDGE_UNINITIALIZED,
"Niko Home Control: no bridge initialized for energy meter " + energyMeterId); "No bridge initialized for energy meter " + energyMeterId);
return null; return null;
} }
NikoHomeControlCommunication nhcComm = nhcBridgeHandler.getCommunication(); NikoHomeControlCommunication nhcComm = nhcBridgeHandler.getCommunication();
@@ -232,7 +245,7 @@ public class NikoHomeControlEnergyMeterHandler extends BaseThingHandler implemen
Bridge nhcBridge = getBridge(); Bridge nhcBridge = getBridge();
if (nhcBridge == null) { if (nhcBridge == null) {
updateStatus(ThingStatus.UNINITIALIZED, ThingStatusDetail.BRIDGE_UNINITIALIZED, updateStatus(ThingStatus.UNINITIALIZED, ThingStatusDetail.BRIDGE_UNINITIALIZED,
"Niko Home Control: no bridge initialized for energy meter " + energyMeterId); "No bridge initialized for energy meter " + energyMeterId);
return null; return null;
} }
NikoHomeControlBridgeHandler nhcBridgeHandler = (NikoHomeControlBridgeHandler) nhcBridge.getHandler(); NikoHomeControlBridgeHandler nhcBridgeHandler = (NikoHomeControlBridgeHandler) nhcBridge.getHandler();

View File

@@ -12,12 +12,15 @@
*/ */
package org.openhab.binding.nikohomecontrol.internal.handler; package org.openhab.binding.nikohomecontrol.internal.handler;
import org.eclipse.jdt.annotation.NonNullByDefault;
/** /**
* {@link NikoHomeControlThermostatConfig} is the config class for Niko Home Control Thermostats. * {@link NikoHomeControlThermostatConfig} is the config class for Niko Home Control Thermostats.
* *
* @author Mark Herwege - Initial Contribution * @author Mark Herwege - Initial Contribution
*/ */
@NonNullByDefault
public class NikoHomeControlThermostatConfig { public class NikoHomeControlThermostatConfig {
public String thermostatId; public String thermostatId = "";
public int overruleTime; public int overruleTime = 60;
} }

View File

@@ -52,7 +52,7 @@ public class NikoHomeControlThermostatHandler extends BaseThingHandler implement
private final Logger logger = LoggerFactory.getLogger(NikoHomeControlThermostatHandler.class); private final Logger logger = LoggerFactory.getLogger(NikoHomeControlThermostatHandler.class);
private volatile @NonNullByDefault({}) NhcThermostat nhcThermostat; private volatile @Nullable NhcThermostat nhcThermostat;
private String thermostatId = ""; private String thermostatId = "";
private int overruleTime; private int overruleTime;
@@ -69,7 +69,7 @@ public class NikoHomeControlThermostatHandler extends BaseThingHandler implement
NikoHomeControlCommunication nhcComm = getCommunication(); NikoHomeControlCommunication nhcComm = getCommunication();
if (nhcComm == null) { if (nhcComm == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"Niko Home Control: bridge communication not initialized when trying to execute thermostat command " "Bridge communication not initialized when trying to execute thermostat command on "
+ thermostatId); + thermostatId);
return; return;
} }
@@ -87,7 +87,13 @@ public class NikoHomeControlThermostatHandler extends BaseThingHandler implement
} }
private void handleCommandSelection(ChannelUID channelUID, Command command) { private void handleCommandSelection(ChannelUID channelUID, Command command) {
logger.debug("Niko Home Control: handle command {} for {}", command, channelUID); NhcThermostat nhcThermostat = this.nhcThermostat;
if (nhcThermostat == null) {
logger.debug("thermostat with ID {} not initialized", thermostatId);
return;
}
logger.debug("handle command {} for {}", command, channelUID);
if (REFRESH.equals(command)) { if (REFRESH.equals(command)) {
thermostatEvent(nhcThermostat.getMeasured(), nhcThermostat.getSetpoint(), nhcThermostat.getMode(), thermostatEvent(nhcThermostat.getMeasured(), nhcThermostat.getSetpoint(), nhcThermostat.getMode(),
@@ -140,7 +146,7 @@ public class NikoHomeControlThermostatHandler extends BaseThingHandler implement
default: default:
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"Niko Home Control: channel unknown " + channelUID.getId()); "Channel unknown " + channelUID.getId());
} }
} }
@@ -153,6 +159,8 @@ public class NikoHomeControlThermostatHandler extends BaseThingHandler implement
NikoHomeControlCommunication nhcComm = getCommunication(); NikoHomeControlCommunication nhcComm = getCommunication();
if (nhcComm == null) { if (nhcComm == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"Connection with controller not started yet, could not initialize thermostat " + thermostatId);
return; return;
} }
@@ -161,16 +169,14 @@ public class NikoHomeControlThermostatHandler extends BaseThingHandler implement
scheduler.submit(() -> { scheduler.submit(() -> {
if (!nhcComm.communicationActive()) { if (!nhcComm.communicationActive()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"Niko Home Control: no connection with Niko Home Control, could not initialize thermostat " "No connection with controller, could not initialize thermostat " + thermostatId);
+ thermostatId);
return; return;
} }
nhcThermostat = nhcComm.getThermostats().get(thermostatId); NhcThermostat nhcThermostat = nhcComm.getThermostats().get(thermostatId);
if (nhcThermostat == null) { if (nhcThermostat == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"Niko Home Control: thermostatId does not match a thermostat in the controller " "Thermostat " + thermostatId + " does not match a thermostat in the controller");
+ thermostatId);
return; return;
} }
@@ -186,7 +192,9 @@ public class NikoHomeControlThermostatHandler extends BaseThingHandler implement
thermostatEvent(nhcThermostat.getMeasured(), nhcThermostat.getSetpoint(), nhcThermostat.getMode(), thermostatEvent(nhcThermostat.getMeasured(), nhcThermostat.getSetpoint(), nhcThermostat.getMode(),
nhcThermostat.getOverrule(), nhcThermostat.getDemand()); nhcThermostat.getOverrule(), nhcThermostat.getDemand());
logger.debug("Niko Home Control: thermostat intialized {}", thermostatId); this.nhcThermostat = nhcThermostat;
logger.debug("thermostat intialized {}", thermostatId);
Bridge bridge = getBridge(); Bridge bridge = getBridge();
if ((bridge != null) && (bridge.getStatus() == ThingStatus.ONLINE)) { if ((bridge != null) && (bridge.getStatus() == ThingStatus.ONLINE)) {
@@ -211,6 +219,12 @@ public class NikoHomeControlThermostatHandler extends BaseThingHandler implement
@Override @Override
public void thermostatEvent(int measured, int setpoint, int mode, int overrule, int demand) { public void thermostatEvent(int measured, int setpoint, int mode, int overrule, int demand) {
NhcThermostat nhcThermostat = this.nhcThermostat;
if (nhcThermostat == null) {
logger.debug("thermostat with ID {} not initialized", thermostatId);
return;
}
updateState(CHANNEL_MEASURED, new QuantityType<>(nhcThermostat.getMeasured() / 10.0, CELSIUS)); updateState(CHANNEL_MEASURED, new QuantityType<>(nhcThermostat.getMeasured() / 10.0, CELSIUS));
int overruletime = nhcThermostat.getRemainingOverruletime(); int overruletime = nhcThermostat.getRemainingOverruletime();
@@ -263,10 +277,18 @@ public class NikoHomeControlThermostatHandler extends BaseThingHandler implement
refreshTimer = null; refreshTimer = null;
} }
@Override
public void thermostatInitialized() {
Bridge bridge = getBridge();
if ((bridge != null) && (bridge.getStatus() == ThingStatus.ONLINE)) {
updateStatus(ThingStatus.ONLINE);
}
}
@Override @Override
public void thermostatRemoved() { public void thermostatRemoved() {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"Niko Home Control: thermostat has been removed from the controller " + thermostatId); "Thermostat " + thermostatId + " has been removed from the controller");
} }
private void restartCommunication(NikoHomeControlCommunication nhcComm) { private void restartCommunication(NikoHomeControlCommunication nhcComm) {
@@ -275,8 +297,7 @@ public class NikoHomeControlThermostatHandler extends BaseThingHandler implement
nhcComm.restartCommunication(); nhcComm.restartCommunication();
// If still not active, take thing offline and return. // If still not active, take thing offline and return.
if (!nhcComm.communicationActive()) { if (!nhcComm.communicationActive()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Communication error");
"Niko Home Control: communication socket error");
return; return;
} }
// Also put the bridge back online // Also put the bridge back online
@@ -290,7 +311,7 @@ public class NikoHomeControlThermostatHandler extends BaseThingHandler implement
NikoHomeControlBridgeHandler nhcBridgeHandler = getBridgeHandler(); NikoHomeControlBridgeHandler nhcBridgeHandler = getBridgeHandler();
if (nhcBridgeHandler == null) { if (nhcBridgeHandler == null) {
updateStatus(ThingStatus.UNINITIALIZED, ThingStatusDetail.BRIDGE_UNINITIALIZED, updateStatus(ThingStatus.UNINITIALIZED, ThingStatusDetail.BRIDGE_UNINITIALIZED,
"Niko Home Control: no bridge initialized for thermostat " + thermostatId); "No bridge initialized for thermostat " + thermostatId);
return null; return null;
} }
NikoHomeControlCommunication nhcComm = nhcBridgeHandler.getCommunication(); NikoHomeControlCommunication nhcComm = nhcBridgeHandler.getCommunication();
@@ -301,7 +322,7 @@ public class NikoHomeControlThermostatHandler extends BaseThingHandler implement
Bridge nhcBridge = getBridge(); Bridge nhcBridge = getBridge();
if (nhcBridge == null) { if (nhcBridge == null) {
updateStatus(ThingStatus.UNINITIALIZED, ThingStatusDetail.BRIDGE_UNINITIALIZED, updateStatus(ThingStatus.UNINITIALIZED, ThingStatusDetail.BRIDGE_UNINITIALIZED,
"Niko Home Control: no bridge initialized for thermostat " + thermostatId); "No bridge initialized for thermostat " + thermostatId);
return null; return null;
} }
NikoHomeControlBridgeHandler nhcBridgeHandler = (NikoHomeControlBridgeHandler) nhcBridge.getHandler(); NikoHomeControlBridgeHandler nhcBridgeHandler = (NikoHomeControlBridgeHandler) nhcBridge.getHandler();

View File

@@ -158,7 +158,7 @@ public abstract class NhcAction {
protected void updateState(int state) { protected void updateState(int state) {
NhcActionEvent eventHandler = this.eventHandler; NhcActionEvent eventHandler = this.eventHandler;
if (eventHandler != null) { if (eventHandler != null) {
logger.debug("Niko Home Control: update channel state for {} with {}", id, state); logger.debug("update channel state for {} with {}", id, state);
eventHandler.actionEvent(state); eventHandler.actionEvent(state);
} }
} }
@@ -167,7 +167,7 @@ public abstract class NhcAction {
* Method called when action is removed from the Niko Home Control Controller. * Method called when action is removed from the Niko Home Control Controller.
*/ */
public void actionRemoved() { public void actionRemoved() {
logger.warn("Niko Home Control: action removed {}, {}", id, name); logger.debug("action removed {}, {}", id, name);
NhcActionEvent eventHandler = this.eventHandler; NhcActionEvent eventHandler = this.eventHandler;
if (eventHandler != null) { if (eventHandler != null) {
eventHandler.actionRemoved(); eventHandler.actionRemoved();

View File

@@ -32,6 +32,12 @@ public interface NhcActionEvent {
*/ */
public void actionEvent(int state); public void actionEvent(int state);
/**
* Called to indicate the action has been initialized.
*
*/
public void actionInitialized();
/** /**
* Called to indicate the action has been removed from the Niko Home Control controller. * Called to indicate the action has been removed from the Niko Home Control controller.
* *

View File

@@ -67,8 +67,9 @@ public interface NhcControllerEvent {
/** /**
* Called to indicate the connection with the Niko Home Control Controller is offline. * Called to indicate the connection with the Niko Home Control Controller is offline.
* *
* @param message
*/ */
public void controllerOffline(); public void controllerOffline(String message);
/** /**
* Called to indicate the connection with the Niko Home Control Controller is online. * Called to indicate the connection with the Niko Home Control Controller is online.

View File

@@ -56,7 +56,7 @@ public abstract class NhcEnergyMeter {
public void updateState(int power) { public void updateState(int power) {
NhcEnergyMeterEvent handler = eventHandler; NhcEnergyMeterEvent handler = eventHandler;
if (handler != null) { if (handler != null) {
logger.debug("Niko Home Control: update channel for {}", id); logger.debug("update channel for {}", id);
handler.energyMeterEvent(power); handler.energyMeterEvent(power);
} }
} }
@@ -65,7 +65,7 @@ public abstract class NhcEnergyMeter {
* Method called when energyMeters meter is removed from the Niko Home Control Controller. * Method called when energyMeters meter is removed from the Niko Home Control Controller.
*/ */
public void energyMeterRemoved() { public void energyMeterRemoved() {
logger.warn("Niko Home Control: action removed {}, {}", id, name); logger.debug("action removed {}, {}", id, name);
NhcEnergyMeterEvent eventHandler = this.eventHandler; NhcEnergyMeterEvent eventHandler = this.eventHandler;
if (eventHandler != null) { if (eventHandler != null) {
eventHandler.energyMeterRemoved(); eventHandler.energyMeterRemoved();
@@ -117,7 +117,7 @@ public abstract class NhcEnergyMeter {
this.power = power; this.power = power;
NhcEnergyMeterEvent handler = eventHandler; NhcEnergyMeterEvent handler = eventHandler;
if (handler != null) { if (handler != null) {
logger.debug("Niko Home Control: update power channel for {} with {}", id, power); logger.debug("update power channel for {} with {}", id, power);
handler.energyMeterEvent(power); handler.energyMeterEvent(power);
} }
} }

View File

@@ -28,14 +28,20 @@ import org.eclipse.jdt.annotation.Nullable;
public interface NhcEnergyMeterEvent { public interface NhcEnergyMeterEvent {
/** /**
* This method is called when an energyMeters meter event is received from the Niko Home Control controller. * This method is called when an energyMeter event is received from the Niko Home Control controller.
* *
* @param power current power consumption/production in W (positive for consumption), null for an empty reading * @param power current power consumption/production in W (positive for consumption), null for an empty reading
*/ */
public void energyMeterEvent(@Nullable Integer power); public void energyMeterEvent(@Nullable Integer power);
/** /**
* Called to indicate the energyMeters meter has been removed from the Niko Home Control controller. * Called to indicate the energyMeter has been initialized.
*
*/
public void energyMeterInitialized();
/**
* Called to indicate the energyMeter has been removed from the Niko Home Control controller.
* *
*/ */
public void energyMeterRemoved(); public void energyMeterRemoved();

View File

@@ -115,7 +115,7 @@ public abstract class NhcThermostat {
* Method called when thermostat is removed from the Niko Home Control Controller. * Method called when thermostat is removed from the Niko Home Control Controller.
*/ */
public void thermostatRemoved() { public void thermostatRemoved() {
logger.warn("Niko Home Control: action removed {}, {}", id, name); logger.debug("action removed {}, {}", id, name);
NhcThermostatEvent eventHandler = this.eventHandler; NhcThermostatEvent eventHandler = this.eventHandler;
if (eventHandler != null) { if (eventHandler != null) {
eventHandler.thermostatRemoved(); eventHandler.thermostatRemoved();
@@ -125,7 +125,7 @@ public abstract class NhcThermostat {
private void updateChannels() { private void updateChannels() {
NhcThermostatEvent handler = eventHandler; NhcThermostatEvent handler = eventHandler;
if (handler != null) { if (handler != null) {
logger.debug("Niko Home Control: update channels for {}", id); logger.debug("update channels for {}", id);
handler.thermostatEvent(measured, setpoint, mode, overrule, demand); handler.thermostatEvent(measured, setpoint, mode, overrule, demand);
} }
} }

View File

@@ -37,6 +37,12 @@ public interface NhcThermostatEvent {
*/ */
public void thermostatEvent(int measured, int setpoint, int mode, int overrule, int demand); public void thermostatEvent(int measured, int setpoint, int mode, int overrule, int demand);
/**
* Called to indicate the thermostat has been initialized.
*
*/
public void thermostatInitialized();
/** /**
* Called to indicate the thermostat has been removed from the Niko Home Control controller. * Called to indicate the thermostat has been removed from the Niko Home Control controller.
* *

View File

@@ -65,7 +65,7 @@ public abstract class NikoHomeControlCommunication {
public synchronized void restartCommunication() { public synchronized void restartCommunication() {
stopCommunication(); stopCommunication();
logger.debug("Niko Home Control: restart communication from thread {}", Thread.currentThread().getId()); logger.debug("restart communication from thread {}", Thread.currentThread().getId());
startCommunication(); startCommunication();
} }

View File

@@ -70,7 +70,7 @@ public final class NikoHomeControlDiscover {
datagramSocket.send(discoveryPacket); datagramSocket.send(discoveryPacket);
while (true) { while (true) {
datagramSocket.receive(packet); datagramSocket.receive(packet);
logger.trace("Niko Home Control: bridge discovery response {}", logger.trace("bridge discovery response {}",
HexUtils.bytesToHex(Arrays.copyOf(packet.getData(), packet.getLength()))); HexUtils.bytesToHex(Arrays.copyOf(packet.getData(), packet.getLength())));
if (isNhc(packet)) { if (isNhc(packet)) {
break; break;
@@ -79,7 +79,7 @@ public final class NikoHomeControlDiscover {
addr = packet.getAddress(); addr = packet.getAddress();
setNhcBridgeId(packet); setNhcBridgeId(packet);
setIsNhcII(packet); setIsNhcII(packet);
logger.debug("Niko Home Control: IP address is {}, unique ID is {}", addr, nhcBridgeId); logger.debug("IP address is {}, unique ID is {}", addr, nhcBridgeId);
} }
} }

View File

@@ -74,7 +74,7 @@ public class NhcAction1 extends NhcAction {
if (getType() == ActionType.ROLLERSHUTTER) { if (getType() == ActionType.ROLLERSHUTTER) {
if (filterEvent) { if (filterEvent) {
filterEvent = false; filterEvent = false;
logger.debug("Niko Home Control: filtered event {} for {}", newState, id); logger.debug("filtered event {} for {}", newState, id);
return; return;
} }
@@ -88,7 +88,7 @@ public class NhcAction1 extends NhcAction {
} }
} }
if (waitForEvent) { if (waitForEvent) {
logger.debug("Niko Home Control: received requested rollershutter {} position event {}", id, newState); logger.debug("received requested rollershutter {} position event {}", id, newState);
executeRollershutterTask(); executeRollershutterTask();
} else { } else {
state = newState; state = newState;
@@ -103,7 +103,7 @@ public class NhcAction1 extends NhcAction {
*/ */
@Override @Override
public void execute(String command) { public void execute(String command) {
logger.debug("Niko Home Control: execute action {} of type {} for {}", command, type, id); logger.debug("execute action {} of type {} for {}", command, type, id);
String value = ""; String value = "";
switch (getType()) { switch (getType()) {
@@ -162,7 +162,7 @@ public class NhcAction1 extends NhcAction {
} else if (command.equals(NHCSTOP)) { } else if (command.equals(NHCSTOP)) {
executeRollershutterStop(); executeRollershutterStop();
} else { } else {
int newValue = 100 - Integer.parseInt(command); int newValue = Integer.parseInt(command);
if (logger.isTraceEnabled()) { if (logger.isTraceEnabled()) {
logger.trace("handleRollerShutterCommand: rollershutter {} percent command, current {}, new {}", id, logger.trace("handleRollerShutterCommand: rollershutter {} percent command, current {}, new {}", id,
currentValue, newValue); currentValue, newValue);
@@ -174,9 +174,9 @@ public class NhcAction1 extends NhcAction {
scheduleRollershutterStop(currentValue, newValue); scheduleRollershutterStop(currentValue, newValue);
} }
if (newValue < currentValue) { if (newValue < currentValue) {
executeRollershutterDown();
} else if (newValue > currentValue) {
executeRollershutterUp(); executeRollershutterUp();
} else if (newValue > currentValue) {
executeRollershutterDown();
} }
} }
}; };

View File

@@ -12,6 +12,8 @@
*/ */
package org.openhab.binding.nikohomecontrol.internal.protocol.nhc1; package org.openhab.binding.nikohomecontrol.internal.protocol.nhc1;
import org.eclipse.jdt.annotation.NonNullByDefault;
/** /**
* Class {@link NhcMessageBase1} used as base class for output from gson for cmd or event feedback from Niko Home * Class {@link NhcMessageBase1} used as base class for output from gson for cmd or event feedback from Niko Home
* Control. This class only contains the common base fields required for the deserializer * Control. This class only contains the common base fields required for the deserializer
@@ -21,10 +23,11 @@ package org.openhab.binding.nikohomecontrol.internal.protocol.nhc1;
* *
* @author Mark Herwege - Initial Contribution * @author Mark Herwege - Initial Contribution
*/ */
@NonNullByDefault
abstract class NhcMessageBase1 { abstract class NhcMessageBase1 {
private String cmd; private String cmd = "";
private String event; private String event = "";
String getCmd() { String getCmd() {
return cmd; return cmd;

View File

@@ -12,6 +12,8 @@
*/ */
package org.openhab.binding.nikohomecontrol.internal.protocol.nhc1; package org.openhab.binding.nikohomecontrol.internal.protocol.nhc1;
import org.eclipse.jdt.annotation.NonNullByDefault;
/** /**
* Class {@link NhcMessageCmd1} used as input to gson to send commands to Niko Home Control. Extends * Class {@link NhcMessageCmd1} used as input to gson to send commands to Niko Home Control. Extends
* {@link NhcMessageBase1}. * {@link NhcMessageBase1}.
@@ -21,6 +23,7 @@ package org.openhab.binding.nikohomecontrol.internal.protocol.nhc1;
* @author Mark Herwege - Initial Contribution * @author Mark Herwege - Initial Contribution
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
@NonNullByDefault
class NhcMessageCmd1 extends NhcMessageBase1 { class NhcMessageCmd1 extends NhcMessageBase1 {
private int id; private int id;
@@ -29,7 +32,7 @@ class NhcMessageCmd1 extends NhcMessageBase1 {
private int value3; private int value3;
private int mode; private int mode;
private int overrule; private int overrule;
private String overruletime; private String overruletime = "";
NhcMessageCmd1(String cmd) { NhcMessageCmd1(String cmd) {
super.setCmd(cmd); super.setCmd(cmd);

View File

@@ -16,6 +16,8 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
/** /**
* Class {@link NhcMessageListMap1} used as output from gson for cmd or event feedback from Niko Home Control where the * Class {@link NhcMessageListMap1} used as output from gson for cmd or event feedback from Niko Home Control where the
* data part is enclosed by [] and contains a list of json strings. Extends {@link NhcMessageBase1}. * data part is enclosed by [] and contains a list of json strings. Extends {@link NhcMessageBase1}.
@@ -25,6 +27,7 @@ import java.util.Map;
* *
* @author Mark Herwege - Initial Contribution * @author Mark Herwege - Initial Contribution
*/ */
@NonNullByDefault
class NhcMessageListMap1 extends NhcMessageBase1 { class NhcMessageListMap1 extends NhcMessageBase1 {
private List<Map<String, String>> data = new ArrayList<>(); private List<Map<String, String>> data = new ArrayList<>();

View File

@@ -15,6 +15,8 @@ package org.openhab.binding.nikohomecontrol.internal.protocol.nhc1;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
/** /**
* Class {@link NhcMessageMap1} used as output from gson for cmd or event feedback from Niko Home Control where the * Class {@link NhcMessageMap1} used as output from gson for cmd or event feedback from Niko Home Control where the
* data part is a simple json string. Extends {@link NhcMessageBase1}. * data part is a simple json string. Extends {@link NhcMessageBase1}.
@@ -23,6 +25,7 @@ import java.util.Map;
* *
* @author Mark Herwege - Initial Contribution * @author Mark Herwege - Initial Contribution
*/ */
@NonNullByDefault
class NhcMessageMap1 extends NhcMessageBase1 { class NhcMessageMap1 extends NhcMessageBase1 {
private Map<String, String> data = new HashMap<>(); private Map<String, String> data = new HashMap<>();

View File

@@ -42,7 +42,7 @@ public class NhcThermostat1 extends NhcThermostat {
*/ */
@Override @Override
public void executeMode(int mode) { public void executeMode(int mode) {
logger.debug("Niko Home Control: execute thermostat mode {} for {}", mode, id); logger.debug("execute thermostat mode {} for {}", mode, id);
nhcComm.executeThermostat(id, Integer.toString(mode)); nhcComm.executeThermostat(id, Integer.toString(mode));
} }
@@ -55,8 +55,7 @@ public class NhcThermostat1 extends NhcThermostat {
*/ */
@Override @Override
public void executeOverrule(int overrule, int overruletime) { public void executeOverrule(int overrule, int overruletime) {
logger.debug("Niko Home Control: execute thermostat overrule {} during {} min for {}", overrule, overruletime, logger.debug("execute thermostat overrule {} during {} min for {}", overrule, overruletime, id);
id);
nhcComm.executeThermostat(id, overrule, overruletime); nhcComm.executeThermostat(id, overrule, overruletime);
} }

View File

@@ -20,6 +20,7 @@ import java.net.InetAddress;
import java.net.Socket; import java.net.Socket;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.function.Consumer; import java.util.function.Consumer;
@@ -94,7 +95,7 @@ public class NikoHomeControlCommunication1 extends NikoHomeControlCommunication
Thread.sleep(1000); Thread.sleep(1000);
} }
if (nhcEventsRunning) { if (nhcEventsRunning) {
logger.debug("Niko Home Control: starting but previous connection still active after 5000ms"); logger.debug("starting but previous connection still active after 5000ms");
throw new IOException(); throw new IOException();
} }
@@ -105,7 +106,7 @@ public class NikoHomeControlCommunication1 extends NikoHomeControlCommunication
nhcSocket = socket; nhcSocket = socket;
nhcOut = new PrintWriter(socket.getOutputStream(), true); nhcOut = new PrintWriter(socket.getOutputStream(), true);
nhcIn = new BufferedReader(new InputStreamReader(socket.getInputStream())); nhcIn = new BufferedReader(new InputStreamReader(socket.getInputStream()));
logger.debug("Niko Home Control: connected via local port {}", socket.getLocalPort()); logger.debug("connected via local port {}", socket.getLocalPort());
// initialize all info in local fields // initialize all info in local fields
initialize(); initialize();
@@ -115,9 +116,8 @@ public class NikoHomeControlCommunication1 extends NikoHomeControlCommunication
(new Thread(this::runNhcEvents)).start(); (new Thread(this::runNhcEvents)).start();
} catch (IOException | InterruptedException e) { } catch (IOException | InterruptedException e) {
logger.warn("Niko Home Control: error initializing communication");
stopCommunication(); stopCommunication();
handler.controllerOffline(); handler.controllerOffline("Error initializing communication");
} }
} }
@@ -139,7 +139,7 @@ public class NikoHomeControlCommunication1 extends NikoHomeControlCommunication
} }
nhcSocket = null; nhcSocket = null;
logger.debug("Niko Home Control: communication stopped"); logger.debug("communication stopped");
} }
@Override @Override
@@ -158,7 +158,7 @@ public class NikoHomeControlCommunication1 extends NikoHomeControlCommunication
private void runNhcEvents() { private void runNhcEvents() {
String nhcMessage; String nhcMessage;
logger.debug("Niko Home Control: listening for events"); logger.debug("listening for events");
listenerStopped = false; listenerStopped = false;
nhcEventsRunning = true; nhcEventsRunning = true;
@@ -170,7 +170,7 @@ public class NikoHomeControlCommunication1 extends NikoHomeControlCommunication
if (!listenerStopped) { if (!listenerStopped) {
nhcEventsRunning = false; nhcEventsRunning = false;
// this is a socket error, not a communication stop triggered from outside this runnable // this is a socket error, not a communication stop triggered from outside this runnable
logger.warn("Niko Home Control: IO error in listener"); logger.debug("IO error in listener");
// the IO has stopped working, so we need to close cleanly and try to restart // the IO has stopped working, so we need to close cleanly and try to restart
restartCommunication(); restartCommunication();
return; return;
@@ -181,7 +181,7 @@ public class NikoHomeControlCommunication1 extends NikoHomeControlCommunication
nhcEventsRunning = false; nhcEventsRunning = false;
// this is a stop from outside the runnable, so just log it and stop // this is a stop from outside the runnable, so just log it and stop
logger.debug("Niko Home Control: event listener thread stopped"); logger.debug("event listener thread stopped");
} }
/** /**
@@ -218,17 +218,16 @@ public class NikoHomeControlCommunication1 extends NikoHomeControlCommunication
@SuppressWarnings("null") @SuppressWarnings("null")
private synchronized void sendMessage(Object nhcMessage) { private synchronized void sendMessage(Object nhcMessage) {
String json = gsonOut.toJson(nhcMessage); String json = gsonOut.toJson(nhcMessage);
logger.debug("Niko Home Control: send json {}", json); logger.debug("send json {}", json);
nhcOut.println(json); nhcOut.println(json);
if (nhcOut.checkError()) { if (nhcOut.checkError()) {
logger.warn("Niko Home Control: error sending message, trying to restart communication"); logger.debug("error sending message, trying to restart communication");
restartCommunication(); restartCommunication();
// retry sending after restart // retry sending after restart
logger.debug("Niko Home Control: resend json {}", json); logger.debug("resend json {}", json);
nhcOut.println(json); nhcOut.println(json);
if (nhcOut.checkError()) { if (nhcOut.checkError()) {
logger.warn("Niko Home Control: error resending message"); handler.controllerOffline("Error resending message");
handler.controllerOffline();
} }
} }
} }
@@ -239,11 +238,14 @@ public class NikoHomeControlCommunication1 extends NikoHomeControlCommunication
* @param nhcMessage message read from Niko Home Control. * @param nhcMessage message read from Niko Home Control.
*/ */
private void readMessage(@Nullable String nhcMessage) { private void readMessage(@Nullable String nhcMessage) {
logger.debug("Niko Home Control: received json {}", nhcMessage); logger.debug("received json {}", nhcMessage);
try { try {
NhcMessageBase1 nhcMessageGson = gsonIn.fromJson(nhcMessage, NhcMessageBase1.class); NhcMessageBase1 nhcMessageGson = gsonIn.fromJson(nhcMessage, NhcMessageBase1.class);
if (nhcMessageGson == null) {
return;
}
String cmd = nhcMessageGson.getCmd(); String cmd = nhcMessageGson.getCmd();
String event = nhcMessageGson.getEvent(); String event = nhcMessageGson.getEvent();
@@ -268,10 +270,10 @@ public class NikoHomeControlCommunication1 extends NikoHomeControlCommunication
} else if ("getalarms".equals(event)) { } else if ("getalarms".equals(event)) {
eventGetAlarms(((NhcMessageMap1) nhcMessageGson).getData()); eventGetAlarms(((NhcMessageMap1) nhcMessageGson).getData());
} else { } else {
logger.debug("Niko Home Control: not acted on json {}", nhcMessage); logger.debug("not acted on json {}", nhcMessage);
} }
} catch (JsonParseException e) { } catch (JsonParseException e) {
logger.debug("Niko Home Control: not acted on unsupported json {}", nhcMessage); logger.debug("not acted on unsupported json {}", nhcMessage);
} }
} }
@@ -283,7 +285,7 @@ public class NikoHomeControlCommunication1 extends NikoHomeControlCommunication
} }
private synchronized void cmdSystemInfo(Map<String, String> data) { private synchronized void cmdSystemInfo(Map<String, String> data) {
logger.debug("Niko Home Control: systeminfo"); logger.debug("systeminfo");
setIfPresent(data, "swversion", systemInfo::setSwVersion); setIfPresent(data, "swversion", systemInfo::setSwVersion);
setIfPresent(data, "api", systemInfo::setApi); setIfPresent(data, "api", systemInfo::setApi);
@@ -311,17 +313,17 @@ public class NikoHomeControlCommunication1 extends NikoHomeControlCommunication
if (errorCodeString != null) { if (errorCodeString != null) {
int errorCode = Integer.parseInt(errorCodeString); int errorCode = Integer.parseInt(errorCodeString);
if (errorCode == 0) { if (errorCode == 0) {
logger.debug("Niko Home Control: start events success"); logger.debug("start events success");
} else { } else {
logger.warn("Niko Home Control: error code {} returned on start events", errorCode); logger.debug("error code {} returned on start events", errorCode);
} }
} else { } else {
logger.warn("Niko Home Control: could not determine error code returned on start events"); logger.debug("could not determine error code returned on start events");
} }
} }
private void cmdListLocations(List<Map<String, String>> data) { private void cmdListLocations(List<Map<String, String>> data) {
logger.debug("Niko Home Control: list locations"); logger.debug("list locations");
locations.clear(); locations.clear();
@@ -338,7 +340,7 @@ public class NikoHomeControlCommunication1 extends NikoHomeControlCommunication
} }
private void cmdListActions(List<Map<String, String>> data) { private void cmdListActions(List<Map<String, String>> data) {
logger.debug("Niko Home Control: list actions"); logger.debug("list actions");
for (Map<String, String> action : data) { for (Map<String, String> action : data) {
String id = action.get("id"); String id = action.get("id");
@@ -360,7 +362,7 @@ public class NikoHomeControlCommunication1 extends NikoHomeControlCommunication
logger.debug("name not found in action {}", action); logger.debug("name not found in action {}", action);
continue; continue;
} }
String type = action.get("type"); String type = Optional.ofNullable(action.get("type")).orElse("");
ActionType actionType = ActionType.GENERIC; ActionType actionType = ActionType.GENERIC;
switch (type) { switch (type) {
case "0": case "0":
@@ -377,7 +379,7 @@ public class NikoHomeControlCommunication1 extends NikoHomeControlCommunication
actionType = ActionType.ROLLERSHUTTER; actionType = ActionType.ROLLERSHUTTER;
break; break;
default: default:
logger.debug("Niko Home Control: unknown action type {} for action {}", type, id); logger.debug("unknown action type {} for action {}", type, id);
continue; continue;
} }
String locationId = action.get("location"); String locationId = action.get("location");
@@ -395,14 +397,18 @@ public class NikoHomeControlCommunication1 extends NikoHomeControlCommunication
// Action object already exists, so only update state. // Action object already exists, so only update state.
// If we would re-instantiate action, we would lose pointer back from action to thing handler that was // If we would re-instantiate action, we would lose pointer back from action to thing handler that was
// set in thing handler initialize(). // set in thing handler initialize().
actions.get(id).setState(state); NhcAction nhcAction = actions.get(id);
if (nhcAction != null) {
nhcAction.setState(state);
}
} }
} }
} }
private int parseIntOrThrow(@Nullable String str) throws IllegalArgumentException { private int parseIntOrThrow(@Nullable String str) throws IllegalArgumentException {
if (str == null) if (str == null) {
throw new IllegalArgumentException("String is null"); throw new IllegalArgumentException("String is null");
}
try { try {
return Integer.parseInt(str); return Integer.parseInt(str);
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
@@ -411,7 +417,7 @@ public class NikoHomeControlCommunication1 extends NikoHomeControlCommunication
} }
private void cmdListThermostat(List<Map<String, String>> data) { private void cmdListThermostat(List<Map<String, String>> data) {
logger.debug("Niko Home Control: list thermostats"); logger.debug("list thermostats");
for (Map<String, String> thermostat : data) { for (Map<String, String> thermostat : data) {
try { try {
@@ -442,8 +448,11 @@ public class NikoHomeControlCommunication1 extends NikoHomeControlCommunication
String name = thermostat.get("name"); String name = thermostat.get("name");
String locationId = thermostat.get("location"); String locationId = thermostat.get("location");
String location = ""; String location = "";
if (!locationId.isEmpty()) { if (!((locationId == null) || locationId.isEmpty())) {
location = locations.get(locationId).getName(); NhcLocation1 nhcLocation = locations.get(locationId);
if (nhcLocation != null) {
location = nhcLocation.getName();
}
} }
if (name != null) { if (name != null) {
return new NhcThermostat1(i, name, location, this); return new NhcThermostat1(i, name, location, this);
@@ -463,12 +472,12 @@ public class NikoHomeControlCommunication1 extends NikoHomeControlCommunication
try { try {
int errorCode = parseIntOrThrow(data.get("error")); int errorCode = parseIntOrThrow(data.get("error"));
if (errorCode == 0) { if (errorCode == 0) {
logger.debug("Niko Home Control: execute action success"); logger.debug("execute action success");
} else { } else {
logger.warn("Niko Home Control: error code {} returned on command execution", errorCode); logger.debug("error code {} returned on command execution", errorCode);
} }
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
logger.warn("Niko Home Control: no error code returned on command execution"); logger.debug("no error code returned on command execution");
} }
} }
@@ -476,12 +485,12 @@ public class NikoHomeControlCommunication1 extends NikoHomeControlCommunication
try { try {
int errorCode = parseIntOrThrow(data.get("error")); int errorCode = parseIntOrThrow(data.get("error"));
if (errorCode == 0) { if (errorCode == 0) {
logger.debug("Niko Home Control: execute thermostats success"); logger.debug("execute thermostats success");
} else { } else {
logger.warn("Niko Home Control: error code {} returned on command execution", errorCode); logger.debug("error code {} returned on command execution", errorCode);
} }
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
logger.warn("Niko Home Control: no error code returned on command execution"); logger.debug("no error code returned on command execution");
} }
} }
@@ -489,13 +498,13 @@ public class NikoHomeControlCommunication1 extends NikoHomeControlCommunication
for (Map<String, String> action : data) { for (Map<String, String> action : data) {
String id = action.get("id"); String id = action.get("id");
if (id == null || !actions.containsKey(id)) { if (id == null || !actions.containsKey(id)) {
logger.warn("Niko Home Control: action in controller not known {}", id); logger.warn("action in controller not known {}", id);
return; return;
} }
String stateString = action.get("value1"); String stateString = action.get("value1");
if (stateString != null) { if (stateString != null) {
int state = Integer.parseInt(stateString); int state = Integer.parseInt(stateString);
logger.debug("Niko Home Control: event execute action {} with state {}", id, state); logger.debug("event execute action {} with state {}", id, state);
NhcAction action1 = actions.get(id); NhcAction action1 = actions.get(id);
if (action1 != null) { if (action1 != null) {
action1.setState(state); action1.setState(state);
@@ -509,7 +518,7 @@ public class NikoHomeControlCommunication1 extends NikoHomeControlCommunication
try { try {
String id = thermostat.get("id"); String id = thermostat.get("id");
if (!thermostats.containsKey(id)) { if (!thermostats.containsKey(id)) {
logger.warn("Niko Home Control: thermostat in controller not known {}", id); logger.warn("thermostat in controller not known {}", id);
return; return;
} }
@@ -549,15 +558,15 @@ public class NikoHomeControlCommunication1 extends NikoHomeControlCommunication
} }
switch (data.getOrDefault("type", "")) { switch (data.getOrDefault("type", "")) {
case "0": case "0":
logger.debug("Niko Home Control: alarm - {}", alarmText); logger.debug("alarm - {}", alarmText);
handler.alarmEvent(alarmText); handler.alarmEvent(alarmText);
break; break;
case "1": case "1":
logger.debug("Niko Home Control: notice - {}", alarmText); logger.debug("notice - {}", alarmText);
handler.noticeEvent(alarmText); handler.noticeEvent(alarmText);
break; break;
default: default:
logger.debug("Niko Home Control: unexpected message type {}", data.get("type")); logger.debug("unexpected message type {}", data.get("type"));
} }
} }

View File

@@ -19,6 +19,9 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import com.google.gson.JsonArray; import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer; import com.google.gson.JsonDeserializer;
@@ -33,16 +36,17 @@ import com.google.gson.JsonParseException;
* @author Mark Herwege - Initial Contribution * @author Mark Herwege - Initial Contribution
* *
*/ */
@NonNullByDefault
class NikoHomeControlMessageDeserializer1 implements JsonDeserializer<NhcMessageBase1> { class NikoHomeControlMessageDeserializer1 implements JsonDeserializer<NhcMessageBase1> {
@Override @Override
public NhcMessageBase1 deserialize(final JsonElement json, final Type typeOfT, public @Nullable NhcMessageBase1 deserialize(final JsonElement json, final Type typeOfT,
final JsonDeserializationContext context) throws JsonParseException { final JsonDeserializationContext context) throws JsonParseException {
final JsonObject jsonObject = json.getAsJsonObject(); final JsonObject jsonObject = json.getAsJsonObject();
try { try {
String cmd = null; String cmd = "";
String event = null; String event = "";
if (jsonObject.has("cmd")) { if (jsonObject.has("cmd")) {
cmd = jsonObject.get("cmd").getAsString(); cmd = jsonObject.get("cmd").getAsString();
} }

View File

@@ -115,7 +115,7 @@ public class NhcAction2 extends NhcAction {
*/ */
@Override @Override
public void execute(String command) { public void execute(String command) {
logger.debug("Niko Home Control: execute action {} of type {} for {}", command, type, id); logger.debug("execute action {} of type {} for {}", command, type, id);
nhcComm.executeAction(id, command); nhcComm.executeAction(id, command);
} }

View File

@@ -100,7 +100,7 @@ public class NhcMqttConnection2 implements MqttActionCallback {
tmFactory.init(keyStore); tmFactory.init(keyStore);
return tmFactory.getTrustManagers(); return tmFactory.getTrustManagers();
} catch (CertificateException | KeyStoreException | NoSuchAlgorithmException | IOException e) { } catch (CertificateException | KeyStoreException | NoSuchAlgorithmException | IOException e) {
logger.warn("Niko Home Control: error with SSL context creation: {} ", e.getMessage()); logger.debug("error with SSL context creation: {} ", e.getMessage());
throw new CertificateException("SSL context creation exception", e); throw new CertificateException("SSL context creation exception", e);
} finally { } finally {
ResourceBundle.clearCache(); ResourceBundle.clearCache();
@@ -121,14 +121,14 @@ public class NhcMqttConnection2 implements MqttActionCallback {
if (future != null) { if (future != null) {
try { try {
future.get(5000, TimeUnit.MILLISECONDS); future.get(5000, TimeUnit.MILLISECONDS);
logger.debug("Niko Home Control: finished stopping connection"); logger.debug("finished stopping connection");
} catch (InterruptedException | ExecutionException | TimeoutException ignore) { } catch (InterruptedException | ExecutionException | TimeoutException ignore) {
logger.debug("Niko Home Control: error stopping connection"); logger.debug("error stopping connection");
} }
stoppedFuture = null; stoppedFuture = null;
} }
logger.debug("Niko Home Control: starting connection..."); logger.debug("starting connection...");
this.cocoAddress = cocoAddress; this.cocoAddress = cocoAddress;
this.port = port; this.port = port;
this.profile = profile; this.profile = profile;
@@ -142,17 +142,17 @@ public class NhcMqttConnection2 implements MqttActionCallback {
subscribedFuture = connection.subscribe("#", messageSubscriber); subscribedFuture = connection.subscribe("#", messageSubscriber);
} }
} else { } else {
logger.debug("Niko Home Control: error connecting"); logger.debug("error connecting");
throw new MqttException("Connection execution exception"); throw new MqttException("Connection execution exception");
} }
} catch (InterruptedException e) { } catch (InterruptedException e) {
logger.debug("Niko Home Control: connection interrupted exception"); logger.debug("connection interrupted exception");
throw new MqttException("Connection interrupted exception"); throw new MqttException("Connection interrupted exception");
} catch (ExecutionException e) { } catch (ExecutionException e) {
logger.debug("Niko Home Control: connection execution exception", e.getCause()); logger.debug("connection execution exception", e.getCause());
throw new MqttException("Connection execution exception"); throw new MqttException("Connection execution exception");
} catch (TimeoutException e) { } catch (TimeoutException e) {
logger.debug("Niko Home Control: connection timeout exception"); logger.debug("connection timeout exception");
throw new MqttException("Connection timeout exception"); throw new MqttException("Connection timeout exception");
} }
} }
@@ -169,7 +169,7 @@ public class NhcMqttConnection2 implements MqttActionCallback {
* Stop the MQTT connection. * Stop the MQTT connection.
*/ */
void stopConnection() { void stopConnection() {
logger.debug("Niko Home Control: stopping connection..."); logger.debug("stopping connection...");
MqttBrokerConnection connection = mqttConnection; MqttBrokerConnection connection = mqttConnection;
if (connection != null) { if (connection != null) {
connection.removeConnectionObserver(connectionObserver); connection.removeConnectionObserver(connectionObserver);
@@ -203,7 +203,7 @@ public class NhcMqttConnection2 implements MqttActionCallback {
try { try {
if ((future != null) && future.get(5000, TimeUnit.MILLISECONDS)) { if ((future != null) && future.get(5000, TimeUnit.MILLISECONDS)) {
MqttConnectionState state = connection.connectionState(); MqttConnectionState state = connection.connectionState();
logger.debug("Niko Home Control: connection state {} for {}", state, connection.getClientId()); logger.debug("connection state {} for {}", state, connection.getClientId());
return state == MqttConnectionState.CONNECTED; return state == MqttConnectionState.CONNECTED;
} }
} catch (InterruptedException | ExecutionException | TimeoutException e) { } catch (InterruptedException | ExecutionException | TimeoutException e) {
@@ -223,25 +223,25 @@ public class NhcMqttConnection2 implements MqttActionCallback {
void connectionPublish(String topic, String payload) throws MqttException { void connectionPublish(String topic, String payload) throws MqttException {
MqttBrokerConnection connection = mqttConnection; MqttBrokerConnection connection = mqttConnection;
if (connection == null) { if (connection == null) {
logger.debug("Niko Home Control: cannot publish, no connection"); logger.debug("cannot publish, no connection");
throw new MqttException("No connection exception"); throw new MqttException("No connection exception");
} }
if (isConnected()) { if (isConnected()) {
logger.debug("Niko Home Control: publish {}, {}", topic, payload); logger.debug("publish {}, {}", topic, payload);
connection.publish(topic, payload.getBytes(), connection.getQos(), false); connection.publish(topic, payload.getBytes(), connection.getQos(), false);
} else { } else {
logger.debug("Niko Home Control: cannot publish, not subscribed to connection messages"); logger.debug("cannot publish, not subscribed to connection messages");
} }
} }
@Override @Override
public void onSuccess(String topic) { public void onSuccess(String topic) {
logger.debug("Niko Home Control: publish succeeded {}", topic); logger.debug("publish succeeded {}", topic);
} }
@Override @Override
public void onFailure(String topic, Throwable error) { public void onFailure(String topic, Throwable error) {
logger.debug("Niko Home Control: publish failed {}, {}", topic, error.getMessage(), error); logger.debug("publish failed {}, {}", topic, error.getMessage(), error);
} }
} }

View File

@@ -45,7 +45,7 @@ public class NhcThermostat2 extends NhcThermostat {
@Override @Override
public void executeMode(int mode) { public void executeMode(int mode) {
logger.debug("Niko Home Control: execute thermostat mode {} for {}", mode, id); logger.debug("execute thermostat mode {} for {}", mode, id);
String program = THERMOSTATMODES[mode]; String program = THERMOSTATMODES[mode];
nhcComm.executeThermostat(id, program); nhcComm.executeThermostat(id, program);
@@ -53,8 +53,7 @@ public class NhcThermostat2 extends NhcThermostat {
@Override @Override
public void executeOverrule(int overrule, int overruletime) { public void executeOverrule(int overrule, int overruletime) {
logger.debug("Niko Home Control: execute thermostat overrule {} during {} min for {}", overrule, overruletime, logger.debug("execute thermostat overrule {} during {} min for {}", overrule, overruletime, id);
id);
nhcComm.executeThermostat(id, overrule, overruletime); nhcComm.executeThermostat(id, overrule, overruletime);
} }

View File

@@ -33,7 +33,11 @@ import java.util.stream.IntStream;
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.nikohomecontrol.internal.protocol.*; import org.openhab.binding.nikohomecontrol.internal.protocol.NhcAction;
import org.openhab.binding.nikohomecontrol.internal.protocol.NhcControllerEvent;
import org.openhab.binding.nikohomecontrol.internal.protocol.NhcEnergyMeter;
import org.openhab.binding.nikohomecontrol.internal.protocol.NhcThermostat;
import org.openhab.binding.nikohomecontrol.internal.protocol.NikoHomeControlCommunication;
import org.openhab.binding.nikohomecontrol.internal.protocol.NikoHomeControlConstants.ActionType; import org.openhab.binding.nikohomecontrol.internal.protocol.NikoHomeControlConstants.ActionType;
import org.openhab.binding.nikohomecontrol.internal.protocol.nhc2.NhcDevice2.NhcProperty; import org.openhab.binding.nikohomecontrol.internal.protocol.nhc2.NhcDevice2.NhcProperty;
import org.openhab.binding.nikohomecontrol.internal.protocol.nhc2.NhcMessage2.NhcMessageParam; import org.openhab.binding.nikohomecontrol.internal.protocol.nhc2.NhcMessage2.NhcMessageParam;
@@ -104,19 +108,19 @@ public class NikoHomeControlCommunication2 extends NikoHomeControlCommunication
InetAddress addr = handler.getAddr(); InetAddress addr = handler.getAddr();
if (addr == null) { if (addr == null) {
logger.warn("Niko Home Control: IP address cannot be empty"); logger.warn("IP address cannot be empty");
stopCommunication(); stopCommunication();
return; return;
} }
String addrString = addr.getHostAddress(); String addrString = addr.getHostAddress();
int port = handler.getPort(); int port = handler.getPort();
logger.debug("Niko Home Control: initializing for mqtt connection to CoCo on {}:{}", addrString, port); logger.debug("initializing for mqtt connection to CoCo on {}:{}", addrString, port);
profile = handler.getProfile(); profile = handler.getProfile();
String token = handler.getToken(); String token = handler.getToken();
if (token.isEmpty()) { if (token.isEmpty()) {
logger.warn("Niko Home Control: JWT token cannot be empty"); logger.warn("JWT token cannot be empty");
stopCommunication(); stopCommunication();
return; return;
} }
@@ -125,7 +129,7 @@ public class NikoHomeControlCommunication2 extends NikoHomeControlCommunication
mqttConnection.startConnection(addrString, port, profile, token); mqttConnection.startConnection(addrString, port, profile, token);
initialize(); initialize();
} catch (MqttException e) { } catch (MqttException e) {
logger.warn("Niko Home Control: error in mqtt communication"); logger.debug("error in mqtt communication");
stopCommunication(); stopCommunication();
} }
} }
@@ -150,7 +154,7 @@ public class NikoHomeControlCommunication2 extends NikoHomeControlCommunication
// Wait until we received all devices info to confirm we are active. // Wait until we received all devices info to confirm we are active.
return started.get(5000, TimeUnit.MILLISECONDS); return started.get(5000, TimeUnit.MILLISECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e) { } catch (InterruptedException | ExecutionException | TimeoutException e) {
logger.debug("Niko Home Control: exception waiting for connection start"); logger.debug("exception waiting for connection start");
return false; return false;
} }
} }
@@ -176,10 +180,10 @@ public class NikoHomeControlCommunication2 extends NikoHomeControlCommunication
mqttConnection.connectionPublish(profile + "/notification/cmd", gson.toJson(message)); mqttConnection.connectionPublish(profile + "/notification/cmd", gson.toJson(message));
} }
private void connectionLost() { private void connectionLost(String message) {
logger.debug("Niko Home Control: connection lost"); logger.debug("connection lost");
stopCommunication(); stopCommunication();
handler.controllerOffline(); handler.controllerOffline(message);
} }
private void systemEvt(String response) { private void systemEvt(String response) {
@@ -189,13 +193,13 @@ public class NikoHomeControlCommunication2 extends NikoHomeControlCommunication
List<NhcSystemInfo2> systemInfo = null; List<NhcSystemInfo2> systemInfo = null;
try { try {
NhcMessage2 message = gson.fromJson(response, messageType); NhcMessage2 message = gson.fromJson(response, messageType);
List<NhcMessageParam> messageParams = message.params; List<NhcMessageParam> messageParams = (message != null) ? message.params : null;
if (messageParams != null) { if (messageParams != null) {
timeInfo = messageParams.stream().filter(p -> (p.timeInfo != null)).findFirst().get().timeInfo; timeInfo = messageParams.stream().filter(p -> (p.timeInfo != null)).findFirst().get().timeInfo;
systemInfo = messageParams.stream().filter(p -> (p.systemInfo != null)).findFirst().get().systemInfo; systemInfo = messageParams.stream().filter(p -> (p.systemInfo != null)).findFirst().get().systemInfo;
} }
} catch (JsonSyntaxException e) { } catch (JsonSyntaxException e) {
logger.debug("Niko Home Control: unexpected json {}", response); logger.debug("unexpected json {}", response);
} catch (NoSuchElementException ignore) { } catch (NoSuchElementException ignore) {
// Ignore if timeInfo not present in response, this should not happen in a timeInfo response // Ignore if timeInfo not present in response, this should not happen in a timeInfo response
} }
@@ -214,12 +218,12 @@ public class NikoHomeControlCommunication2 extends NikoHomeControlCommunication
List<NhcSystemInfo2> systemInfo = null; List<NhcSystemInfo2> systemInfo = null;
try { try {
NhcMessage2 message = gson.fromJson(response, messageType); NhcMessage2 message = gson.fromJson(response, messageType);
List<NhcMessageParam> messageParams = message.params; List<NhcMessageParam> messageParams = (message != null) ? message.params : null;
if (messageParams != null) { if (messageParams != null) {
systemInfo = messageParams.stream().filter(p -> (p.systemInfo != null)).findFirst().get().systemInfo; systemInfo = messageParams.stream().filter(p -> (p.systemInfo != null)).findFirst().get().systemInfo;
} }
} catch (JsonSyntaxException e) { } catch (JsonSyntaxException e) {
logger.debug("Niko Home Control: unexpected json {}", response); logger.debug("unexpected json {}", response);
} catch (NoSuchElementException ignore) { } catch (NoSuchElementException ignore) {
// Ignore if systemInfo not present in response, this should not happen in a systemInfo response // Ignore if systemInfo not present in response, this should not happen in a systemInfo response
} }
@@ -234,12 +238,12 @@ public class NikoHomeControlCommunication2 extends NikoHomeControlCommunication
List<NhcService2> serviceList = null; List<NhcService2> serviceList = null;
try { try {
NhcMessage2 message = gson.fromJson(response, messageType); NhcMessage2 message = gson.fromJson(response, messageType);
List<NhcMessageParam> messageParams = message.params; List<NhcMessageParam> messageParams = (message != null) ? message.params : null;
if (messageParams != null) { if (messageParams != null) {
serviceList = messageParams.stream().filter(p -> (p.services != null)).findFirst().get().services; serviceList = messageParams.stream().filter(p -> (p.services != null)).findFirst().get().services;
} }
} catch (JsonSyntaxException e) { } catch (JsonSyntaxException e) {
logger.debug("Niko Home Control: unexpected json {}", response); logger.debug("unexpected json {}", response);
} catch (NoSuchElementException ignore) { } catch (NoSuchElementException ignore) {
// Ignore if services not present in response, this should not happen in a services response // Ignore if services not present in response, this should not happen in a services response
} }
@@ -255,12 +259,12 @@ public class NikoHomeControlCommunication2 extends NikoHomeControlCommunication
List<NhcDevice2> deviceList = null; List<NhcDevice2> deviceList = null;
try { try {
NhcMessage2 message = gson.fromJson(response, messageType); NhcMessage2 message = gson.fromJson(response, messageType);
List<NhcMessageParam> messageParams = message.params; List<NhcMessageParam> messageParams = (message != null) ? message.params : null;
if (messageParams != null) { if (messageParams != null) {
deviceList = messageParams.stream().filter(p -> (p.devices != null)).findFirst().get().devices; deviceList = messageParams.stream().filter(p -> (p.devices != null)).findFirst().get().devices;
} }
} catch (JsonSyntaxException e) { } catch (JsonSyntaxException e) {
logger.debug("Niko Home Control: unexpected json {}", response); logger.debug("unexpected json {}", response);
} catch (NoSuchElementException ignore) { } catch (NoSuchElementException ignore) {
// Ignore if devices not present in response, this should not happen in a devices response // Ignore if devices not present in response, this should not happen in a devices response
} }
@@ -274,7 +278,7 @@ public class NikoHomeControlCommunication2 extends NikoHomeControlCommunication
} }
// Once a devices list response is received, we know the communication is fully started. // Once a devices list response is received, we know the communication is fully started.
logger.debug("Niko Home Control: Communication start complete."); logger.debug("Communication start complete.");
handler.controllerOnline(); handler.controllerOnline();
CompletableFuture<Boolean> future = communicationStarted; CompletableFuture<Boolean> future = communicationStarted;
if (future != null) { if (future != null) {
@@ -289,13 +293,13 @@ public class NikoHomeControlCommunication2 extends NikoHomeControlCommunication
String method = null; String method = null;
try { try {
NhcMessage2 message = gson.fromJson(response, messageType); NhcMessage2 message = gson.fromJson(response, messageType);
method = message.method; method = (message != null) ? message.method : null;
List<NhcMessageParam> messageParams = message.params; List<NhcMessageParam> messageParams = (message != null) ? message.params : null;
if (messageParams != null) { if (messageParams != null) {
deviceList = messageParams.stream().filter(p -> (p.devices != null)).findFirst().get().devices; deviceList = messageParams.stream().filter(p -> (p.devices != null)).findFirst().get().devices;
} }
} catch (JsonSyntaxException e) { } catch (JsonSyntaxException e) {
logger.debug("Niko Home Control: unexpected json {}", response); logger.debug("unexpected json {}", response);
} catch (NoSuchElementException ignore) { } catch (NoSuchElementException ignore) {
// Ignore if devices not present in response, this should not happen in a devices event // Ignore if devices not present in response, this should not happen in a devices event
} }
@@ -308,9 +312,6 @@ public class NikoHomeControlCommunication2 extends NikoHomeControlCommunication
return; return;
} else if ("devices.added".equals(method)) { } else if ("devices.added".equals(method)) {
deviceList.forEach(this::addDevice); deviceList.forEach(this::addDevice);
} else if ("devices.changed".equals(method)) {
deviceList.forEach(this::removeDevice);
deviceList.forEach(this::addDevice);
} }
deviceList.forEach(this::updateState); deviceList.forEach(this::updateState);
@@ -322,17 +323,17 @@ public class NikoHomeControlCommunication2 extends NikoHomeControlCommunication
List<NhcNotification2> notificationList = null; List<NhcNotification2> notificationList = null;
try { try {
NhcMessage2 message = gson.fromJson(response, messageType); NhcMessage2 message = gson.fromJson(response, messageType);
List<NhcMessageParam> messageParams = message.params; List<NhcMessageParam> messageParams = (message != null) ? message.params : null;
if (messageParams != null) { if (messageParams != null) {
notificationList = messageParams.stream().filter(p -> (p.notifications != null)).findFirst() notificationList = messageParams.stream().filter(p -> (p.notifications != null)).findFirst()
.get().notifications; .get().notifications;
} }
} catch (JsonSyntaxException e) { } catch (JsonSyntaxException e) {
logger.debug("Niko Home Control: unexpected json {}", response); logger.debug("unexpected json {}", response);
} catch (NoSuchElementException ignore) { } catch (NoSuchElementException ignore) {
// Ignore if notifications not present in response, this should not happen in a notifications event // Ignore if notifications not present in response, this should not happen in a notifications event
} }
logger.debug("Niko Home Control: notifications {}", notificationList); logger.debug("notifications {}", notificationList);
if (notificationList == null) { if (notificationList == null) {
return; return;
} }
@@ -348,7 +349,7 @@ public class NikoHomeControlCommunication2 extends NikoHomeControlCommunication
handler.noticeEvent(alarmText); handler.noticeEvent(alarmText);
break; break;
default: default:
logger.debug("Niko Home Control: unexpected message type {}", notification.type); logger.debug("unexpected message type {}", notification.type);
} }
} }
} }
@@ -363,7 +364,7 @@ public class NikoHomeControlCommunication2 extends NikoHomeControlCommunication
if ("action".equals(device.type)) { if ("action".equals(device.type)) {
if (!actions.containsKey(device.uuid)) { if (!actions.containsKey(device.uuid)) {
logger.debug("Niko Home Control: adding action device {}, {}", device.uuid, device.name); logger.debug("adding action device {}, {}", device.uuid, device.name);
ActionType actionType; ActionType actionType;
switch (device.model) { switch (device.model) {
@@ -394,8 +395,7 @@ public class NikoHomeControlCommunication2 extends NikoHomeControlCommunication
break; break;
default: default:
actionType = ActionType.GENERIC; actionType = ActionType.GENERIC;
logger.debug("Niko Home Control: device type {} not recognised, default to GENERIC action", logger.debug("device type {} not recognised, default to GENERIC action", device.type);
device.type);
} }
NhcAction2 nhcAction = new NhcAction2(device.uuid, device.name, device.model, device.technology, NhcAction2 nhcAction = new NhcAction2(device.uuid, device.name, device.model, device.technology,
@@ -404,7 +404,7 @@ public class NikoHomeControlCommunication2 extends NikoHomeControlCommunication
} }
} else if ("thermostat".equals(device.type)) { } else if ("thermostat".equals(device.type)) {
if (!thermostats.containsKey(device.uuid)) { if (!thermostats.containsKey(device.uuid)) {
logger.debug("Niko Home Control: adding thermostat device {}, {}", device.uuid, device.name); logger.debug("adding thermostat device {}, {}", device.uuid, device.name);
NhcThermostat2 nhcThermostat = new NhcThermostat2(device.uuid, device.name, device.model, NhcThermostat2 nhcThermostat = new NhcThermostat2(device.uuid, device.name, device.model,
device.technology, location, this); device.technology, location, this);
@@ -412,26 +412,28 @@ public class NikoHomeControlCommunication2 extends NikoHomeControlCommunication
} }
} else if ("centralmeter".equals(device.type)) { } else if ("centralmeter".equals(device.type)) {
if (!energyMeters.containsKey(device.uuid)) { if (!energyMeters.containsKey(device.uuid)) {
logger.debug("Niko Home Control: adding centralmeter device {}, {}", device.uuid, device.name); logger.debug("adding centralmeter device {}, {}", device.uuid, device.name);
NhcEnergyMeter2 nhcEnergyMeter = new NhcEnergyMeter2(device.uuid, device.name, device.model, NhcEnergyMeter2 nhcEnergyMeter = new NhcEnergyMeter2(device.uuid, device.name, device.model,
device.technology, this, scheduler); device.technology, this, scheduler);
energyMeters.put(device.uuid, nhcEnergyMeter); energyMeters.put(device.uuid, nhcEnergyMeter);
} }
} else { } else {
logger.debug("Niko Home Control: device type {} not supported for {}, {}", device.type, device.uuid, logger.debug("device type {} not supported for {}, {}", device.type, device.uuid, device.name);
device.name);
} }
} }
private void removeDevice(NhcDevice2 device) { private void removeDevice(NhcDevice2 device) {
if (actions.containsKey(device.uuid)) { NhcAction action = actions.get(device.uuid);
actions.get(device.uuid).actionRemoved(); NhcThermostat thermostat = thermostats.get(device.uuid);
NhcEnergyMeter energyMeter = energyMeters.get(device.uuid);
if (action != null) {
action.actionRemoved();
actions.remove(device.uuid); actions.remove(device.uuid);
} else if (thermostats.containsKey(device.uuid)) { } else if (thermostat != null) {
thermostats.get(device.uuid).thermostatRemoved(); thermostat.thermostatRemoved();
thermostats.remove(device.uuid); thermostats.remove(device.uuid);
} else if (energyMeters.containsKey(device.uuid)) { } else if (energyMeter != null) {
energyMeters.get(device.uuid).energyMeterRemoved(); energyMeter.energyMeterRemoved();
energyMeters.remove(device.uuid); energyMeters.remove(device.uuid);
} }
} }
@@ -481,10 +483,10 @@ public class NikoHomeControlCommunication2 extends NikoHomeControlCommunication
if (booleanState != null) { if (booleanState != null) {
if (NHCON.equals(booleanState)) { if (NHCON.equals(booleanState)) {
action.setBooleanState(true); action.setBooleanState(true);
logger.debug("Niko Home Control: setting action {} internally to ON", action.getId()); logger.debug("setting action {} internally to ON", action.getId());
} else if (NHCOFF.equals(booleanState)) { } else if (NHCOFF.equals(booleanState)) {
action.setBooleanState(false); action.setBooleanState(false);
logger.debug("Niko Home Control: setting action {} internally to OFF", action.getId()); logger.debug("setting action {} internally to OFF", action.getId());
} }
} }
@@ -492,8 +494,7 @@ public class NikoHomeControlCommunication2 extends NikoHomeControlCommunication
String brightness = dimmerProperty.get().brightness; String brightness = dimmerProperty.get().brightness;
if (brightness != null) { if (brightness != null) {
action.setState(Integer.parseInt(brightness)); action.setState(Integer.parseInt(brightness));
logger.debug("Niko Home Control: setting action {} internally to {}", action.getId(), logger.debug("setting action {} internally to {}", action.getId(), dimmerProperty.get().brightness);
dimmerProperty.get().brightness);
} }
} }
} }
@@ -502,9 +503,9 @@ public class NikoHomeControlCommunication2 extends NikoHomeControlCommunication
deviceProperties.stream().map(p -> p.position).filter(Objects::nonNull).findFirst().ifPresent(position -> { deviceProperties.stream().map(p -> p.position).filter(Objects::nonNull).findFirst().ifPresent(position -> {
try { try {
action.setState(Integer.parseInt(position)); action.setState(Integer.parseInt(position));
logger.debug("Niko Home Control: setting action {} internally to {}", action.getId(), position); logger.debug("setting action {} internally to {}", action.getId(), position);
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
logger.trace("Niko Home Control: received empty rollershutter {} position info", action.getId()); logger.trace("received empty rollershutter {} position info", action.getId());
} }
}); });
} }
@@ -577,12 +578,10 @@ public class NikoHomeControlCommunication2 extends NikoHomeControlCommunication
.ifPresent(electricalPower -> { .ifPresent(electricalPower -> {
try { try {
energyMeter.setPower(Integer.parseInt(electricalPower)); energyMeter.setPower(Integer.parseInt(electricalPower));
logger.trace("Niko Home Control: setting energy meter {} power to {}", energyMeter.getId(), logger.trace("setting energy meter {} power to {}", energyMeter.getId(), electricalPower);
electricalPower);
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
energyMeter.setPower(null); energyMeter.setPower(null);
logger.trace("Niko Home Control: received empty energy meter {} power reading", logger.trace("received empty energy meter {} power reading", energyMeter.getId());
energyMeter.getId());
} }
}); });
} }
@@ -607,6 +606,9 @@ public class NikoHomeControlCommunication2 extends NikoHomeControlCommunication
device.properties = deviceProperties; device.properties = deviceProperties;
NhcAction2 action = (NhcAction2) actions.get(actionId); NhcAction2 action = (NhcAction2) actions.get(actionId);
if (action == null) {
return;
}
switch (action.getType()) { switch (action.getType()) {
case GENERIC: case GENERIC:
@@ -639,7 +641,7 @@ public class NikoHomeControlCommunication2 extends NikoHomeControlCommunication
} else if (NHCDOWN.equals(value)) { } else if (NHCDOWN.equals(value)) {
property.position = "0"; property.position = "0";
} else { } else {
int position = 100 - Integer.parseInt(value); int position = Integer.parseInt(value);
property.position = String.valueOf(position); property.position = String.valueOf(position);
} }
break; break;
@@ -745,12 +747,18 @@ public class NikoHomeControlCommunication2 extends NikoHomeControlCommunication
String topic = profile + "/control/devices/cmd"; String topic = profile + "/control/devices/cmd";
String gsonMessage = gson.toJson(message); String gsonMessage = gson.toJson(message);
((NhcEnergyMeter2) energyMeters.get(energyMeterId)).startEnergyMeter(topic, gsonMessage); NhcEnergyMeter2 energyMeter = (NhcEnergyMeter2) energyMeters.get(energyMeterId);
if (energyMeter != null) {
energyMeter.startEnergyMeter(topic, gsonMessage);
}
} }
@Override @Override
public void stopEnergyMeter(String energyMeterId) { public void stopEnergyMeter(String energyMeterId) {
((NhcEnergyMeter2) energyMeters.get(energyMeterId)).stopEnergyMeter(); NhcEnergyMeter2 energyMeter = (NhcEnergyMeter2) energyMeters.get(energyMeterId);
if (energyMeter != null) {
energyMeter.stopEnergyMeter();
}
} }
/** /**
@@ -768,19 +776,26 @@ public class NikoHomeControlCommunication2 extends NikoHomeControlCommunication
mqttConnection.connectionPublish(topic, gsonMessage); mqttConnection.connectionPublish(topic, gsonMessage);
} catch (MqttException e) { } catch (MqttException e) {
logger.warn("Niko Home Control: sending command failed, trying to restart communication"); String message = e.getMessage();
message = (message != null) ? message : "Communication error";
logger.debug("sending command failed, trying to restart communication");
restartCommunication(); restartCommunication();
// retry sending after restart // retry sending after restart
try { try {
if (communicationActive()) { if (communicationActive()) {
mqttConnection.connectionPublish(topic, gsonMessage); mqttConnection.connectionPublish(topic, gsonMessage);
} else { } else {
logger.warn("Niko Home Control: failed to restart communication"); logger.debug("failed to restart communication");
connectionLost();
} }
} catch (MqttException e1) { } catch (MqttException e1) {
logger.warn("Niko Home Control: error resending device command"); message = e1.getMessage();
connectionLost(); message = (message != null) ? message : "Communication error";
logger.debug("error resending device command");
}
if (!communicationActive()) {
connectionLost(message);
} }
} }
} }
@@ -791,24 +806,24 @@ public class NikoHomeControlCommunication2 extends NikoHomeControlCommunication
if ((profile + "/system/evt").equals(topic)) { if ((profile + "/system/evt").equals(topic)) {
systemEvt(message); systemEvt(message);
} else if ((profile + "/system/rsp").equals(topic)) { } else if ((profile + "/system/rsp").equals(topic)) {
logger.debug("Niko Home Control: received topic {}, payload {}", topic, message); logger.debug("received topic {}, payload {}", topic, message);
systeminfoPublishRsp(message); systeminfoPublishRsp(message);
} else if ((profile + "/notification/evt").equals(topic)) { } else if ((profile + "/notification/evt").equals(topic)) {
logger.debug("Niko Home Control: received topic {}, payload {}", topic, message); logger.debug("received topic {}, payload {}", topic, message);
notificationEvt(message); notificationEvt(message);
} else if ((profile + "/control/devices/evt").equals(topic)) { } else if ((profile + "/control/devices/evt").equals(topic)) {
logger.trace("Niko Home Control: received topic {}, payload {}", topic, message); logger.trace("received topic {}, payload {}", topic, message);
devicesEvt(message); devicesEvt(message);
} else if ((profile + "/control/devices/rsp").equals(topic)) { } else if ((profile + "/control/devices/rsp").equals(topic)) {
logger.debug("Niko Home Control: received topic {}, payload {}", topic, message); logger.debug("received topic {}, payload {}", topic, message);
devicesListRsp(message); devicesListRsp(message);
} else if ((profile + "/authentication/rsp").equals(topic)) { } else if ((profile + "/authentication/rsp").equals(topic)) {
logger.debug("Niko Home Control: received topic {}, payload {}", topic, message); logger.debug("received topic {}, payload {}", topic, message);
servicesListRsp(message); servicesListRsp(message);
} else if ((profile + "/control/devices.error").equals(topic)) { } else if ((profile + "/control/devices.error").equals(topic)) {
logger.warn("Niko Home Control: received error {}", message); logger.warn("received error {}", message);
} else { } else {
logger.trace("Niko Home Control: not acted on received message topic {}, payload {}", topic, message); logger.trace("not acted on received message topic {}, payload {}", topic, message);
} }
} }
@@ -845,10 +860,14 @@ public class NikoHomeControlCommunication2 extends NikoHomeControlCommunication
public void connectionStateChanged(MqttConnectionState state, @Nullable Throwable error) { public void connectionStateChanged(MqttConnectionState state, @Nullable Throwable error) {
if (error != null) { if (error != null) {
logger.debug("Connection state: {}", state, error); logger.debug("Connection state: {}", state, error);
String message = error.getMessage();
message = (message != null) ? message : "Error communicating with the controller";
if (!MqttConnectionState.CONNECTING.equals(state)) {
// This is a connection loss, try to restart
restartCommunication(); restartCommunication();
}
if (!communicationActive()) { if (!communicationActive()) {
logger.warn("Niko Home Control: failed to restart communication"); connectionLost(message);
connectionLost();
} }
} else { } else {
logger.trace("Connection state: {}", state); logger.trace("Connection state: {}", state);

View File

@@ -127,7 +127,7 @@
<description>Niko Home Control action ID</description> <description>Niko Home Control action ID</description>
<advanced>false</advanced> <advanced>false</advanced>
</parameter> </parameter>
<parameter name="step" type="integer" required="true"> <parameter name="step" type="integer">
<label>Step Value</label> <label>Step Value</label>
<description>Step value used for increase/decrease of dimmer brightness, default 10%</description> <description>Step value used for increase/decrease of dimmer brightness, default 10%</description>
<default>10</default> <default>10</default>
@@ -151,6 +151,12 @@
<description>Niko Home Control action ID</description> <description>Niko Home Control action ID</description>
<advanced>false</advanced> <advanced>false</advanced>
</parameter> </parameter>
<parameter name="invert" type="boolean">
<label>Invert Direction</label>
<description>Invert rollershutter direction</description>
<default>false</default>
<advanced>true</advanced>
</parameter>
</config-description> </config-description>
</thing-type> </thing-type>
<thing-type id="thermostat"> <thing-type id="thermostat">