private @Nullable NeoHubReadDcbResponse systemData = null;
- private boolean isLegacyApiSelected = true;
+ private enum ApiVersion {
+ LEGACY("legacy"),
+ NEW("new");
+
+ public final String label;
+
+ private ApiVersion(String label) {
+ this.label = label;
+ }
+ }
+
+ private ApiVersion apiVersion = ApiVersion.LEGACY;
private boolean isApiOnline = false;
public NeoHubHandler(Bridge bridge) {
NeoHubConfiguration config = getConfigAs(NeoHubConfiguration.class);
if (logger.isDebugEnabled()) {
- logger.debug("hostname={}", config.hostName);
+ logger.debug("hub '{}' hostname={}", getThing().getUID(), config.hostName);
}
if (!MATCHER_IP_ADDRESS.matcher(config.hostName).matches()) {
}
if (logger.isDebugEnabled()) {
- logger.debug("port={}", config.portNumber);
+ logger.debug("hub '{}' port={}", getThing().getUID(), config.portNumber);
}
if (config.portNumber <= 0 || config.portNumber > 0xFFFF) {
}
if (logger.isDebugEnabled()) {
- logger.debug("polling interval={}", config.pollingInterval);
+ logger.debug("hub '{}' polling interval={}", getThing().getUID(), config.pollingInterval);
}
if (config.pollingInterval < FAST_POLL_INTERVAL || config.pollingInterval > LAZY_POLL_INTERVAL) {
}
if (logger.isDebugEnabled()) {
- logger.debug("socketTimeout={}", config.socketTimeout);
+ logger.debug("hub '{}' socketTimeout={}", getThing().getUID(), config.socketTimeout);
}
if (config.socketTimeout < 5 || config.socketTimeout > 20) {
}
if (logger.isDebugEnabled()) {
- logger.debug("preferLegacyApi={}", config.preferLegacyApi);
+ logger.debug("hub '{}' preferLegacyApi={}", getThing().getUID(), config.preferLegacyApi);
}
socket = new NeoHubSocket(config.hostName, config.portNumber, config.socketTimeout);
this.config = config;
if (logger.isDebugEnabled()) {
- logger.debug("start background polling..");
+ logger.debug("hub '{}' start background polling..", getThing().getUID());
}
// create a "lazy" polling scheduler
@Override
public void dispose() {
if (logger.isDebugEnabled()) {
- logger.debug("stop background polling..");
+ logger.debug("hub '{}' stop background polling..", getThing().getUID());
}
// clean up the lazy polling scheduler
return NeoHubReturnResult.SUCCEEDED;
} catch (Exception e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
- logger.warn(MSG_FMT_SET_VALUE_ERR, commandStr, e.getMessage());
+ logger.warn(MSG_FMT_SET_VALUE_ERR, getThing().getUID(), commandStr, e.getMessage());
return NeoHubReturnResult.ERR_COMMUNICATION;
}
}
NeoHubSocket socket = this.socket;
if (socket == null || config == null) {
- logger.warn(MSG_HUB_CONFIG);
+ logger.warn(MSG_HUB_CONFIG, getThing().getUID());
return null;
}
String responseJson;
NeoHubAbstractDeviceData deviceData;
- if (isLegacyApiSelected) {
+ if (apiVersion == ApiVersion.LEGACY) {
responseJson = socket.sendMessage(CMD_CODE_INFO);
deviceData = NeoHubInfoResponse.createDeviceData(responseJson);
} else {
}
if (deviceData == null) {
- logger.warn(MSG_FMT_DEVICE_POLL_ERR, "failed to create device data response");
+ logger.warn(MSG_FMT_DEVICE_POLL_ERR, getThing().getUID(), "failed to create device data response");
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
return null;
}
@Nullable
List<? extends AbstractRecord> devices = deviceData.getDevices();
if (devices == null || devices.isEmpty()) {
- logger.warn(MSG_FMT_DEVICE_POLL_ERR, "no devices found");
+ logger.warn(MSG_FMT_DEVICE_POLL_ERR, getThing().getUID(), "no devices found");
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
return null;
}
return deviceData;
} catch (Exception e) {
- logger.warn(MSG_FMT_DEVICE_POLL_ERR, e.getMessage());
+ logger.warn(MSG_FMT_DEVICE_POLL_ERR, getThing().getUID(), e.getMessage());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
return null;
}
String responseJson;
NeoHubReadDcbResponse systemData;
- if (isLegacyApiSelected) {
+ if (apiVersion == ApiVersion.LEGACY) {
responseJson = socket.sendMessage(CMD_CODE_READ_DCB);
systemData = NeoHubReadDcbResponse.createSystemData(responseJson);
} else {
}
if (systemData == null) {
- logger.warn(MSG_FMT_SYSTEM_POLL_ERR, "failed to create system data response");
+ logger.warn(MSG_FMT_SYSTEM_POLL_ERR, getThing().getUID(), "failed to create system data response");
return null;
}
+ String physicalFirmware = systemData.getFirmwareVersion();
+ if (physicalFirmware != null) {
+ String thingFirmware = getThing().getProperties().get(PROPERTY_FIRMWARE_VERSION);
+ if (!physicalFirmware.equals(thingFirmware)) {
+ getThing().setProperty(PROPERTY_FIRMWARE_VERSION, physicalFirmware);
+ }
+ }
+
return systemData;
} catch (Exception e) {
- logger.warn(MSG_FMT_SYSTEM_POLL_ERR, e.getMessage());
+ logger.warn(MSG_FMT_SYSTEM_POLL_ERR, getThing().getUID(), e.getMessage());
return null;
}
}
// evaluate and update the state of our RF mesh QoS channel
List<? extends AbstractRecord> devices = deviceData.getDevices();
State state;
+ String property;
if (devices == null || devices.isEmpty()) {
state = UnDefType.UNDEF;
+ property = "[?/?]";
} else {
int totalDeviceCount = devices.size();
int onlineDeviceCount = 0;
@Nullable
Boolean onlineBefore = connectionStates.put(deviceName, online);
- if (!online.equals(onlineBefore)) {
- logger.info("device \"{}\" has {} the RF mesh network", deviceName,
- online.booleanValue() ? "joined" : "left");
+ /*
+ * note: we use logger.info() here to log changes; reason is that the average user does really need
+ * to know if a device (very occasionally) drops out of the normally reliable RF mesh; however we
+ * only log it if 1) the state has changed, and 2) either 2a) the device has already been discovered
+ * by the bridge handler, or 2b) logger debug mode is set
+ */
+ if (!online.equals(onlineBefore) && ((onlineBefore != null) || logger.isDebugEnabled())) {
+ logger.info("hub '{}' device \"{}\" has {} the RF mesh network", getThing().getUID(),
+ deviceName, online.booleanValue() ? "joined" : "left");
}
if (online.booleanValue()) {
onlineDeviceCount++;
}
}
+ property = String.format("[%d/%d]", onlineDeviceCount, totalDeviceCount);
state = new QuantityType<>((100.0 * onlineDeviceCount) / totalDeviceCount, Units.PERCENT);
}
+ getThing().setProperty(PROPERTY_API_DEVICEINFO, property);
updateState(CHAN_MESH_NETWORK_QOS, state);
}
if (fastPollingCallsToGo.get() > 0) {
}
} catch (JsonSyntaxException | NeoHubException | IOException e) {
// we learned that this API is not currently supported; no big deal
- logger.debug("Legacy API is not supported!");
+ logger.debug("hub '{}' legacy API is not supported!", getThing().getUID());
}
try {
responseJson = socket.sendMessage(CMD_CODE_GET_SYSTEM);
systemData = NeoHubReadDcbResponse.createSystemData(responseJson);
supportsFutureApi = systemData != null;
if (!supportsFutureApi) {
- throw new NeoHubException("new API not supported");
+ throw new NeoHubException(String.format("hub '%s' new API not supported", getThing().getUID()));
}
} catch (JsonSyntaxException | NeoHubException | IOException e) {
// we learned that this API is not currently supported; no big deal
- logger.debug("New API is not supported!");
+ logger.debug("hub '{}' new API is not supported!", getThing().getUID());
}
}
if (!supportsLegacyApi && !supportsFutureApi) {
- logger.warn("Currently neither legacy nor new API are supported!");
+ logger.warn("hub '{}' currently neither legacy nor new API are supported!", getThing().getUID());
isApiOnline = false;
return;
}
NeoHubConfiguration config = this.config;
- boolean isLegacyApiSelected = (supportsLegacyApi && config != null && config.preferLegacyApi);
- if (isLegacyApiSelected != this.isLegacyApiSelected) {
- logger.info("Changing API version: {}",
- isLegacyApiSelected ? "\"new\" => \"legacy\"" : "\"legacy\" => \"new\"");
+ ApiVersion apiVersion = (supportsLegacyApi && config != null && config.preferLegacyApi) ? ApiVersion.LEGACY
+ : ApiVersion.NEW;
+ if (apiVersion != this.apiVersion) {
+ logger.debug("hub '{}' changing API version: '{}' => '{}'", getThing().getUID(), this.apiVersion.label,
+ apiVersion.label);
+ this.apiVersion = apiVersion;
}
- this.isLegacyApiSelected = isLegacyApiSelected;
+
+ if (!apiVersion.label.equals(getThing().getProperties().get(PROPERTY_API_VERSION))) {
+ getThing().setProperty(PROPERTY_API_VERSION, apiVersion.label);
+ }
+
this.isApiOnline = true;
}
responseJson = socket.sendMessage(CMD_CODE_GET_ENGINEERS);
return NeoHubGetEngineersData.createEngineersData(responseJson);
} catch (JsonSyntaxException | IOException | NeoHubException e) {
- logger.warn(MSG_FMT_ENGINEERS_POLL_ERR, e.getMessage());
+ logger.warn(MSG_FMT_ENGINEERS_POLL_ERR, getThing().getUID(), e.getMessage());
}
}
return null;
}
public boolean isLegacyApiSelected() {
- return isLegacyApiSelected;
+ return apiVersion == ApiVersion.LEGACY;
}
public Unit<?> getTemperatureUnit() {
import java.io.IOException;
import java.math.BigDecimal;
import java.time.Instant;
+import java.util.regex.Pattern;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
@NonNullByDefault
public class NeoHubTestData {
+ /*
+ * to actually run tests on a physical device you must have a hub physically available, and its IP address must be
+ * correctly configured in the "hubIPAddress" string constant e.g. "192.168.1.123"
+ * note: only run the test if such a device is actually available
+ */
+ private static final String hubIpAddress = "192.168.1.xxx";
+
+ private static final Pattern VALID_IP_V4_ADDRESS = Pattern
+ .compile("\\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\\.|$)){4}\\b");
+
/*
* Load the test JSON payload string from a file
*/
/*
* Test an INFO JSON response string as produced by older firmware versions
*/
+ @SuppressWarnings("null")
@Test
public void testInfoJsonOld() {
// load INFO JSON response string in old JSON format
/*
* Test an INFO JSON response string as produced by newer firmware versions
*/
+ @SuppressWarnings("null")
@Test
public void testInfoJsonNew() {
// load INFO JSON response string in new JSON format
/*
* Test for a READ_DCB JSON string that has valid CORF C response
*/
+ @SuppressWarnings("null")
@Test
public void testReadDcbJson() {
// load READ_DCB JSON response string with valid CORF C response
NeoHubReadDcbResponse dcbResponse = NeoHubReadDcbResponse.createSystemData(load("dcb_celsius"));
assertNotNull(dcbResponse);
assertEquals(SIUnits.CELSIUS, dcbResponse.getTemperatureUnit());
+ assertEquals("2134", dcbResponse.getFirmwareVersion());
// load READ_DCB JSON response string with valid CORF F response
dcbResponse = NeoHubReadDcbResponse.createSystemData(load("dcb_fahrenheit"));
/*
* Test an INFO JSON string that has a door contact and a temperature sensor
*/
+ @SuppressWarnings("null")
@Test
public void testInfoJsonWithSensors() {
/*
* From NeoHub rev2.6 onwards the READ_DCB command is "deprecated" so we can
* also test the replacement GET_SYSTEM command (valid CORF response)
*/
+ @SuppressWarnings("null")
@Test
public void testGetSystemJson() {
// load GET_SYSTEM JSON response string
dcbResponse = NeoHubReadDcbResponse.createSystemData(load("system"));
assertNotNull(dcbResponse);
assertEquals(SIUnits.CELSIUS, dcbResponse.getTemperatureUnit());
+ assertEquals("2134", dcbResponse.getFirmwareVersion());
}
/*
* From NeoHub rev2.6 onwards the INFO command is "deprecated" so we must test
* the replacement GET_LIVE_DATA command
*/
+ @SuppressWarnings("null")
@Test
public void testGetLiveDataJson() {
// load GET_LIVE_DATA JSON response string
* element is not returned in the GET_LIVE_DATA call so we must test the
* replacement GET_ENGINEERS command
*/
+ @SuppressWarnings("null")
@Test
public void testGetEngineersJson() {
// load GET_ENGINEERS JSON response string
* send JSON request to the socket and retrieve JSON response
*/
private String testCommunicationInner(String requestJson) {
- NeoHubSocket socket = new NeoHubSocket("192.168.1.109", 4242, 5);
+ NeoHubSocket socket = new NeoHubSocket(hubIpAddress, 4242, 5);
String responseJson = "";
try {
responseJson = socket.sendMessage(requestJson);
/*
* Test the communications
*/
+ @SuppressWarnings("null")
@Test
public void testCommunications() {
+ /*
+ * tests the actual communication with a real physical device on 'hubIpAddress'
+ * note: only run the test if such a device is actually available
+ */
+ if (!VALID_IP_V4_ADDRESS.matcher(hubIpAddress).matches()) {
+ return;
+ }
+
String responseJson = testCommunicationInner(CMD_CODE_INFO);
assertFalse(responseJson.isEmpty());