added migrated 2.x add-ons

Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
Kai Kreuzer
2020-09-21 01:58:32 +02:00
parent bbf1a7fd29
commit 6df6783b60
11662 changed files with 1302875 additions and 11 deletions

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.neohub-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
<feature name="openhab-binding-neohub" description="NeoHub Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.neohub/${project.version}</bundle>
</feature>
</features>

View File

@@ -0,0 +1,26 @@
/**
* Copyright (c) 2010-2020 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.neohub.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link NeoBaseConfiguration} class contains the thing configuration
* parameters for NeoStat and NeoPlug devices
*
* @author Andrew Fiddian-Green - Initial contribution
*/
@NonNullByDefault
public class NeoBaseConfiguration {
public String deviceNameInHub = "";
}

View File

@@ -0,0 +1,286 @@
/**
* Copyright (c) 2010-2020 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.neohub.internal;
import static org.openhab.binding.neohub.internal.NeoHubBindingConstants.*;
import javax.measure.Unit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.neohub.internal.NeoHubAbstractDeviceData.AbstractRecord;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.thing.binding.BridgeHandler;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link NeoBaseHandler} is the openHAB Handler for NeoPlug devices
*
* @author Andrew Fiddian-Green - Initial contribution
*
*/
@NonNullByDefault
public class NeoBaseHandler extends BaseThingHandler {
protected final Logger logger = LoggerFactory.getLogger(NeoBaseHandler.class);
protected @Nullable NeoBaseConfiguration config;
/*
* error messages
*/
private static final String MSG_FMT_DEVICE_CONFIG = "device \"{}\" needs to configured in hub!";
private static final String MSG_FMT_DEVICE_COMM = "device \"{}\" not communicating with hub!";
private static final String MSG_FMT_COMMAND_OK = "command for \"{}\" succeeded.";
private static final String MSG_FMT_COMMAND_BAD = "\"{}\" is an invalid or empty command!";
private static final String MSG_DEVICE_NAME_NOT_CONFIGURED = "the parameter \"deviceNameInHub\" is not configured";
/*
* an object used to de-bounce state changes between openHAB and the NeoHub
*/
protected NeoHubDebouncer debouncer = new NeoHubDebouncer();
public NeoBaseHandler(Thing thing) {
super(thing);
}
// ======== BaseThingHandler methods that are overridden =============
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
@Nullable
NeoHubHandler hub;
if ((hub = getNeoHub()) != null) {
if (command == RefreshType.REFRESH) {
hub.startFastPollingBurst();
return;
}
}
toNeoHubSendCommandSet(channelUID.getId(), command);
}
@Override
public void initialize() {
NeoBaseConfiguration config = getConfigAs(NeoBaseConfiguration.class);
if (config.deviceNameInHub.isEmpty()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, MSG_DEVICE_NAME_NOT_CONFIGURED);
return;
}
this.config = config;
NeoHubHandler hub = getNeoHub();
if (hub == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, MSG_HUB_CONFIG);
return;
}
updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.CONFIGURATION_PENDING);
}
// ======== helper methods used by this class or descendants ===========
/*
* this method is called back by the NeoHub handler to inform this handler about
* polling results from the hub handler
*/
public void toBaseSendPollResponse(NeoHubAbstractDeviceData deviceData) {
NeoBaseConfiguration config = this.config;
if (config == null) {
return;
}
AbstractRecord deviceRecord = deviceData.getDeviceRecord(config.deviceNameInHub);
if (deviceRecord == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
logger.warn(MSG_FMT_DEVICE_CONFIG, thing.getLabel());
return;
}
ThingStatus thingStatus = getThing().getStatus();
if (deviceRecord.offline() && (thingStatus == ThingStatus.ONLINE)) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
logger.debug(MSG_FMT_DEVICE_COMM, thing.getLabel());
return;
}
if ((!deviceRecord.offline()) && (thingStatus != ThingStatus.ONLINE)) {
updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE);
}
toOpenHabSendChannelValues(deviceRecord);
}
/*
* internal method used by by sendChannelValuesToOpenHab(); it checks the
* de-bouncer before actually sending the channel value to openHAB; or if the
* device has lost its connection to the RF mesh, either a) send no updates to
* OpenHAB or b) send state = undefined, depending on the value of a
* Configuration Parameter
*/
protected void toOpenHabSendValueDebounced(String channelId, State state, boolean offline) {
/*
* if the device has been lost from the RF mesh network there are two possible
* behaviors: either a) do not report a state value, or b) show an undefined
* state; the choice of a) or b) depends on whether the channel has the
* Configuration Parameter holdOnlineState=true
*/
if (!offline) {
if (debouncer.timeExpired(channelId)) {
/*
* in normal circumstances just forward the hub's reported state to OpenHAB
*/
updateState(channelId, state);
}
} else {
ChannelUID channelUID = new ChannelUID(thing.getUID(), channelId);
Channel channel = thing.getChannel(channelUID);
if (channel != null) {
Configuration config = channel.getConfiguration();
Object holdOnlineState = config.get(PARAM_HOLD_ONLINE_STATE);
if (holdOnlineState != null && (holdOnlineState instanceof Boolean)
&& ((Boolean) holdOnlineState).booleanValue()) {
/*
* the Configuration Parameter "holdOnlineState" is True so do NOT send a
* state update to OpenHAB
*/
return;
}
}
/*
* the Configuration Parameter "holdOnlineState" is either not existing or
* it is False so send a state=undefined update to OpenHAB
*/
updateState(channelUID, UnDefType.UNDEF);
}
}
/*
* sends a channel command & value from openHAB => NeoHub. It delegates upwards
* to the NeoHub to handle the command
*/
protected void toNeoHubSendCommand(String channelId, Command command) {
String cmdStr = toNeoHubBuildCommandString(channelId, command);
if (!cmdStr.isEmpty()) {
NeoHubHandler hub = getNeoHub();
if (hub != null) {
/*
* issue command, check result, and update status accordingly
*/
switch (hub.toNeoHubSendChannelValue(cmdStr)) {
case SUCCEEDED:
logger.debug(MSG_FMT_COMMAND_OK, getThing().getLabel());
if (getThing().getStatus() != ThingStatus.ONLINE) {
updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE);
}
// initialize the de-bouncer for this channel
debouncer.initialize(channelId);
break;
case ERR_COMMUNICATION:
logger.debug(MSG_HUB_COMM);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
break;
case ERR_INITIALIZATION:
logger.warn(MSG_HUB_CONFIG);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
break;
}
} else {
logger.debug(MSG_HUB_CONFIG);
}
} else {
logger.debug(MSG_FMT_COMMAND_BAD, command.toString());
}
}
/**
* internal getter returns the NeoHub handler
*
* @return the neohub handler or null
*/
protected @Nullable NeoHubHandler getNeoHub() {
@Nullable
Bridge b;
@Nullable
BridgeHandler h;
if ((b = getBridge()) != null && (h = b.getHandler()) != null && h instanceof NeoHubHandler) {
return (NeoHubHandler) h;
}
return null;
}
// ========= methods that MAY / MUST be overridden in descendants ============
/*
* NOTE: descendant classes MUST override this method. It builds the command
* string to be sent to the NeoHub
*/
protected String toNeoHubBuildCommandString(String channelId, Command command) {
return "";
}
/*
* NOTE: descendant classes MAY override this method e.g. to send additional
* commands for dependent channels (if any)
*/
protected void toNeoHubSendCommandSet(String channelId, Command command) {
toNeoHubSendCommand(channelId, command);
}
/*
* NOTE: descendant classes MUST override this method method by which the
* handler informs openHAB about channel state changes
*/
protected void toOpenHabSendChannelValues(AbstractRecord deviceRecord) {
}
protected OnOffType invert(OnOffType value) {
return OnOffType.from(value == OnOffType.OFF);
}
protected Unit<?> getTemperatureUnit() {
@Nullable
NeoHubHandler hub = getNeoHub();
if (hub != null) {
return hub.getTemperatureUnit();
}
return SIUnits.CELSIUS;
}
}

View File

@@ -0,0 +1,49 @@
/**
* Copyright (c) 2010-2020 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.neohub.internal;
import static org.openhab.binding.neohub.internal.NeoHubBindingConstants.*;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.neohub.internal.NeoHubAbstractDeviceData.AbstractRecord;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.OpenClosedType;
import org.openhab.core.thing.Thing;
/**
* The {@link NeoContactHandler} is the OpenHAB Handler for NeoContact devices
*
* Note: inherits almost all the functionality of a {@link NeoBaseHandler}
*
* @author Andrew Fiddian-Green - Initial contribution
*
*/
@NonNullByDefault
public class NeoContactHandler extends NeoBaseHandler {
public NeoContactHandler(Thing thing) {
super(thing);
}
// =========== methods of NeoBaseHandler that are overridden ================
@Override
protected void toOpenHabSendChannelValues(AbstractRecord deviceRecord) {
boolean offline = deviceRecord.offline();
toOpenHabSendValueDebounced(CHAN_CONTACT_STATE,
deviceRecord.isWindowOpen() ? OpenClosedType.OPEN : OpenClosedType.CLOSED, offline);
toOpenHabSendValueDebounced(CHAN_BATTERY_LOW_ALARM, OnOffType.from(deviceRecord.isBatteryLow()), offline);
}
}

View File

@@ -0,0 +1,77 @@
/**
* Copyright (c) 2010-2020 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.neohub.internal;
import java.math.BigDecimal;
import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* An abstract prototype for wrappers around JSON responses to JSON INFO or
* GET_LIVE_DATA requests
*
* @author Andrew Fiddian-Green - Initial contribution
*/
@NonNullByDefault
public abstract class NeoHubAbstractDeviceData {
@SuppressWarnings("null")
@NonNullByDefault
public abstract static class AbstractRecord {
public abstract String getDeviceName();
public abstract BigDecimal getTargetTemperature();
public abstract BigDecimal getActualTemperature();
public abstract BigDecimal getFloorTemperature();
public abstract boolean isStandby();
public abstract boolean isHeating();
public abstract boolean isPreHeating();
public abstract boolean isTimerOn();
public abstract boolean offline();
public abstract boolean stateManual();
public abstract boolean stateAuto();
public abstract boolean isWindowOpen();
public abstract boolean isBatteryLow();
protected BigDecimal safeBigDecimal(@Nullable BigDecimal value) {
return value != null ? value : BigDecimal.ZERO;
}
}
/**
* returns the device record corresponding to a given device name
*
* @param deviceName the device name
* @return its respective device record
*/
public abstract @Nullable AbstractRecord getDeviceRecord(String deviceName);
/**
* @return the full list of device records
*/
public abstract @Nullable List<? extends AbstractRecord> getDevices();
}

View File

@@ -0,0 +1,191 @@
/**
* Copyright (c) 2010-2020 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.neohub.internal;
import java.util.regex.Pattern;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ThingTypeUID;
/**
* The {@link NeoHubBindingConstants} class defines common constants
*
* @author Sebastian Prehn - Initial contribution (NeoHub command codes)
* @author Andrew Fiddian-Green - Initial contribution (OpenHAB v2.x binding
* code)
*
*/
@NonNullByDefault
public class NeoHubBindingConstants {
/*
* binding id
*/
public static final String BINDING_ID = "neohub";
/*
* device id's
*/
public static final String DEVICE_ID_NEOHUB = "neohub";
public static final String DEVICE_ID_NEOSTAT = "neostat";
public static final String DEVICE_ID_NEOPLUG = "neoplug";
public static final String DEVICE_ID_NEOCONTACT = "neocontact";
public static final String DEVICE_ID_NEOTEMPERATURESENSOR = "neotemperaturesensor";
/*
* Thing Type UIDs
*/
public static final ThingTypeUID THING_TYPE_NEOHUB = new ThingTypeUID(BINDING_ID, DEVICE_ID_NEOHUB);
public static final ThingTypeUID THING_TYPE_NEOSTAT = new ThingTypeUID(BINDING_ID, DEVICE_ID_NEOSTAT);
public static final ThingTypeUID THING_TYPE_NEOPLUG = new ThingTypeUID(BINDING_ID, DEVICE_ID_NEOPLUG);
public static final ThingTypeUID THING_TYPE_NEOCONTACT = new ThingTypeUID(BINDING_ID, DEVICE_ID_NEOCONTACT);
public static final ThingTypeUID THING_TYPE_NEOTEMPERATURESENSOR = new ThingTypeUID(BINDING_ID,
DEVICE_ID_NEOTEMPERATURESENSOR);
/*
* Channel IDs for NeoHub
*/
public static final String CHAN_MESH_NETWORK_QOS = "meshNetworkQoS";
/*
* Channel IDs common for several device types
*/
public static final String CHAN_BATTERY_LOW_ALARM = "batteryLowAlarm";
/*
* Channel IDs for NeoStat thermostats
*/
public static final String CHAN_ROOM_TEMP = "roomTemperature";
public static final String CHAN_TARGET_TEMP = "targetTemperature";
public static final String CHAN_FLOOR_TEMP = "floorTemperature";
public static final String CHAN_OCC_MODE_PRESENT = "occupancyModePresent";
public static final String CHAN_STAT_OUTPUT_STATE = "thermostatOutputState";
/*
* Channel IDs for NeoPlug smart plugs
*/
public static final String CHAN_PLUG_OUTPUT_STATE = "plugOutputState";
public static final String CHAN_PLUG_AUTO_MODE = "plugAutoMode";
/*
* Channel IDs for NeoContact (wireless) contact sensors
*/
public static final String CHAN_CONTACT_STATE = "contactState";
/*
* Channel IDs for NeoTemperatureSensor (wireless) temperature sensors
*/
public static final String CHAN_TEMPERATURE_SENSOR = "sensorTemperature";
/*
* Heatmiser Device Types
*/
public static final int HEATMISER_DEVICE_TYPE_CONTACT = 5;
public static final int HEATMISER_DEVICE_TYPE_PLUG = 6;
public static final int HEATMISER_DEVICE_TYPE_REPEATER = 10;
public static final int HEATMISER_DEVICE_TYPE_TEMPERATURE_SENSOR = 14;
/*
* configuration parameters
*/
public static final String PARAM_HOLD_ONLINE_STATE = "holdOnlineState";
/*
* regular expression pattern matchers
*/
public static final Pattern MATCHER_HEATMISER_REPEATER = Pattern.compile("^repeaternode\\d+");
public static final Pattern MATCHER_IP_ADDRESS = Pattern
.compile("\\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\\.|$)){4}\\b");
/*
* enumerator for results of method calls
*/
public static enum NeoHubReturnResult {
SUCCEEDED,
ERR_COMMUNICATION,
ERR_INITIALIZATION
}
/*
* the property IdD for the name of a thing in the NeoHub note: names may differ
* between the NeoHub and the OpenHAB framework
*/
public static final String DEVICE_NAME = "deviceNameInHub";
/*
* setup parameters for de-bouncing of state changes (time in seconds) so state
* changes that occur within this time window are ignored
*/
public static final long DEBOUNCE_DELAY = 15;
/*
* setup parameters for lazy polling
*/
public static final int LAZY_POLL_INTERVAL = 60;
/*
* setup parameters for fast polling bursts a burst comprises FAST_POLL_CYCLES
* polling calls spaced at FAST_POLL_INTERVAL for example 5 polling calls made
* at 4 second intervals (e.g. 5 x 4 => 20 seconds)
*/
public static final int FAST_POLL_CYCLES = 5;
public static final int FAST_POLL_INTERVAL = 4;
/*
* setup parameters for device discovery
*/
public static final int DISCOVERY_TIMEOUT = 5;
public static final int DISCOVERY_START_DELAY = 30;
public static final int DISCOVERY_REFRESH_PERIOD = 600;
/*
* NeoHub JSON command codes strings: Thanks to Sebastian Prehn !!
*/
public static final String CMD_CODE_INFO = "{\"INFO\":0}";
public static final String CMD_CODE_TEMP = "{\"SET_TEMP\":[%s, \"%s\"]}";
public static final String CMD_CODE_AWAY = "{\"FROST_%s\":\"%s\"}";
public static final String CMD_CODE_TIMER = "{\"TIMER_%s\":\"%s\"}";
public static final String CMD_CODE_MANUAL = "{\"MANUAL_%s\":\"%s\"}";
public static final String CMD_CODE_READ_DCB = "{\"READ_DCB\":100}";
/*
* note: from NeoHub rev2.6 onwards the INFO command is "deprecated" and it
* should be replaced partly each by the new GET_LIVE_DATA and GET_ENGINEERS
* commands
*/
public static final String CMD_CODE_GET_LIVE_DATA = "{\"GET_LIVE_DATA\":0}";
public static final String CMD_CODE_GET_ENGINEERS = "{\"GET_ENGINEERS\":0}";
/*
* note: from NeoHub rev2.6 onwards the READ_DCB command is "deprecated" and it
* should be replaced by the new GET_SYSTEM command
*/
public static final String CMD_CODE_GET_SYSTEM = "{\"GET_SYSTEM\":0}";
/*
* openHAB status strings
*/
public static final String VAL_OFF = "Off";
public static final String VAL_HEATING = "Heating";
/*
* logger message strings
*/
public static final String PLEASE_REPORT_BUG = "Unexpected situation - please report a bug: ";
public static final String MSG_HUB_CONFIG = PLEASE_REPORT_BUG + "hub needs to be initialized!";
public static final String MSG_HUB_COMM = PLEASE_REPORT_BUG + "error communicating with the hub!";
public static final String MSG_FMT_DEVICE_POLL_ERR = "Device data polling error: {}";
public static final String MSG_FMT_SYSTEM_POLL_ERR = "System data polling error: {}";
public static final String MSG_FMT_ENGINEERS_POLL_ERR = "Engineers data polling error: {}";
public static final String MSG_FMT_SET_VALUE_ERR = "{} set value error: {}";
}

View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) 2010-2020 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.neohub.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link NeoHubConfiguration} class contains the thing configuration
* parameters
*
* @author Andrew Fiddian-Green - Initial contribution
*/
@NonNullByDefault
public class NeoHubConfiguration {
public String hostName = "";
public int portNumber;
public int pollingInterval;
public int socketTimeout;
public boolean preferLegacyApi;
}

View File

@@ -0,0 +1,62 @@
/**
* Copyright (c) 2010-2020 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.neohub.internal;
import static org.openhab.binding.neohub.internal.NeoHubBindingConstants.DEBOUNCE_DELAY;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link NeoHubDebouncer} determines if change events should be forwarded
* to a channel
*
* @author Andrew Fiddian-Green - Initial contribution
*/
@NonNullByDefault
public class NeoHubDebouncer {
private final Map<String, DebounceDelay> channels = new HashMap<>();
@SuppressWarnings("null")
@NonNullByDefault
static class DebounceDelay {
private long expireTime;
public DebounceDelay(Boolean enabled) {
if (enabled) {
expireTime = new Date().getTime() + (DEBOUNCE_DELAY * 1000);
}
}
public boolean timeExpired() {
return (expireTime < new Date().getTime());
}
}
public NeoHubDebouncer() {
}
public void initialize(String channelId) {
channels.put(channelId, new DebounceDelay(true));
}
@SuppressWarnings("null")
public boolean timeExpired(String channelId) {
return (channels.containsKey(channelId) ? channels.get(channelId).timeExpired() : true);
}
}

View File

@@ -0,0 +1,186 @@
/**
* Copyright (c) 2010-2020 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.neohub.internal;
import static org.openhab.binding.neohub.internal.NeoHubBindingConstants.*;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.neohub.internal.NeoHubAbstractDeviceData.AbstractRecord;
import org.openhab.binding.neohub.internal.NeoHubInfoResponse.InfoRecord;
import org.openhab.binding.neohub.internal.NeoHubLiveDeviceData.LiveDataRecord;
import org.openhab.core.config.discovery.AbstractDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Discovery service for neo devices
*
* @author Andrew Fiddian-Green - Initial contribution
*
*/
@NonNullByDefault
public class NeoHubDiscoveryService extends AbstractDiscoveryService {
private final Logger logger = LoggerFactory.getLogger(NeoHubDiscoveryService.class);
private @Nullable ScheduledFuture<?> discoveryScheduler;
private NeoHubHandler hub;
public static final Set<ThingTypeUID> DISCOVERABLE_THING_TYPES_UIDS = Collections.unmodifiableSet(
Stream.of(THING_TYPE_NEOSTAT, THING_TYPE_NEOPLUG, THING_TYPE_NEOCONTACT, THING_TYPE_NEOTEMPERATURESENSOR)
.collect(Collectors.toSet()));
public NeoHubDiscoveryService(NeoHubHandler hub) {
// note: background discovery is enabled in the super method
super(DISCOVERABLE_THING_TYPES_UIDS, DISCOVERY_TIMEOUT);
this.hub = hub;
}
public void activate() {
super.activate(null);
}
@Override
public void deactivate() {
super.deactivate();
}
@Override
protected void startScan() {
if (hub.getThing().getStatus() == ThingStatus.ONLINE) {
discoverDevices();
}
}
@Override
protected void startBackgroundDiscovery() {
logger.debug("start background discovery..");
ScheduledFuture<?> discoveryScheduler = this.discoveryScheduler;
if (discoveryScheduler == null || discoveryScheduler.isCancelled()) {
this.discoveryScheduler = scheduler.scheduleWithFixedDelay(this::startScan, 10, DISCOVERY_REFRESH_PERIOD,
TimeUnit.SECONDS);
}
}
@Override
protected void stopBackgroundDiscovery() {
logger.debug("stop background discovery..");
ScheduledFuture<?> discoveryScheduler = this.discoveryScheduler;
if (discoveryScheduler != null && !discoveryScheduler.isCancelled()) {
discoveryScheduler.cancel(true);
}
}
private void discoverDevices() {
NeoHubAbstractDeviceData deviceData = hub.fromNeoHubGetDeviceData();
NeoHubGetEngineersData engineerData = hub.isLegacyApiSelected() ? null : hub.fromNeoHubGetEngineersData();
if (deviceData != null) {
List<? extends AbstractRecord> deviceRecords = deviceData.getDevices();
if (deviceRecords != null) {
int deviceType;
for (AbstractRecord deviceRecord : deviceRecords) {
// the record came from the legacy API (deviceType included)
if (deviceRecord instanceof InfoRecord) {
deviceType = ((InfoRecord) deviceRecord).getDeviceType();
publishDevice((InfoRecord) deviceRecord, deviceType);
continue;
}
// the record came from the now API (deviceType NOT included)
if (deviceRecord instanceof LiveDataRecord) {
if (engineerData == null) {
break;
}
String deviceName = ((LiveDataRecord) deviceRecord).getDeviceName();
// exclude repeater nodes from being discovered
if (MATCHER_HEATMISER_REPEATER.matcher(deviceName).matches()) {
continue;
}
deviceType = engineerData.getDeviceType(deviceName);
publishDevice((LiveDataRecord) deviceRecord, deviceType);
}
}
}
}
}
private void publishDevice(AbstractRecord device, int deviceId) {
if (deviceId <= 0) {
return;
}
String deviceType;
String deviceOpenHabId;
String deviceNeohubName;
ThingUID deviceUID;
ThingTypeUID deviceTypeUID;
DiscoveryResult discoveredDevice;
ThingUID bridgeUID = hub.getThing().getUID();
switch (deviceId) {
case HEATMISER_DEVICE_TYPE_CONTACT: {
deviceType = DEVICE_ID_NEOCONTACT;
deviceTypeUID = THING_TYPE_NEOCONTACT;
break;
}
case HEATMISER_DEVICE_TYPE_PLUG: {
deviceType = DEVICE_ID_NEOPLUG;
deviceTypeUID = THING_TYPE_NEOPLUG;
break;
}
case HEATMISER_DEVICE_TYPE_TEMPERATURE_SENSOR: {
deviceType = DEVICE_ID_NEOTEMPERATURESENSOR;
deviceTypeUID = THING_TYPE_NEOTEMPERATURESENSOR;
break;
}
// all other device types are assumed to be thermostats
default: {
deviceType = DEVICE_ID_NEOSTAT;
deviceTypeUID = THING_TYPE_NEOSTAT;
}
}
deviceNeohubName = device.getDeviceName();
deviceOpenHabId = deviceNeohubName.replaceAll("\\s+", "_");
deviceUID = new ThingUID(deviceTypeUID, bridgeUID, deviceOpenHabId);
discoveredDevice = DiscoveryResultBuilder.create(deviceUID).withBridge(bridgeUID).withLabel(deviceOpenHabId)
.withProperty(DEVICE_NAME, deviceNeohubName).withRepresentationProperty(DEVICE_NAME).build();
thingDiscovered(discoveredDevice);
logger.debug("discovered device: id={}, type={}, name={} ..", deviceId, deviceType, deviceOpenHabId);
}
}

View File

@@ -0,0 +1,31 @@
/**
* Copyright (c) 2010-2020 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.neohub.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link NeoHubException} is a custom exception for NeoHub
*
* @author Andrew Fiddian-Green - Initial contribution
*
*/
@NonNullByDefault
public class NeoHubException extends Exception {
private static final long serialVersionUID = -7358712540781217363L;
public NeoHubException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,92 @@
/**
* Copyright (c) 2010-2020 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.neohub.internal;
import java.math.BigDecimal;
import java.util.HashMap;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.google.gson.annotations.SerializedName;
/**
* A wrapper around the JSON response to the JSON GET_ENGINEERS request
*
* @author Andrew Fiddian-Green - Initial contribution
*/
@NonNullByDefault
public class NeoHubGetEngineersData {
private static final Gson GSON = new Gson();
@SuppressWarnings("null")
@NonNullByDefault
private static class EngineersRecords extends HashMap<String, EngineersRecord> {
private static final long serialVersionUID = 1L;
}
@SuppressWarnings("null")
@NonNullByDefault
public class EngineersRecord {
@SerializedName("DEVICE_TYPE")
private @Nullable BigDecimal deviceType;
public int getDeviceType() {
BigDecimal deviceType = this.deviceType;
return deviceType != null ? deviceType.intValue() : -1;
}
}
private @Nullable EngineersRecords deviceRecords;
/**
* Create wrapper around a JSON string
*
* @param fromJson the JSON string
* @return a NeoHubGetEngData wrapper around the JSON string
* @throws JsonSyntaxException
*
*/
public NeoHubGetEngineersData(String fromJson) throws JsonSyntaxException {
deviceRecords = GSON.fromJson(fromJson, EngineersRecords.class);
}
public static @Nullable NeoHubGetEngineersData createEngineersData(String fromJson) throws JsonSyntaxException {
return new NeoHubGetEngineersData(fromJson);
}
/**
* returns the device record corresponding to a given device name
*
* @param deviceName the device name
* @return its respective device information record
*/
private @Nullable EngineersRecord getDevice(String deviceName) {
EngineersRecords deviceRecords = this.deviceRecords;
return deviceRecords != null ? deviceRecords.get(deviceName) : null;
}
/**
* returns the deviceType corresponding to a given device name
*
* @param deviceName the device name
* @return its respective device information record
*/
public int getDeviceType(String deviceName) {
EngineersRecord record = getDevice(deviceName);
return record != null ? record.getDeviceType() : -1;
}
}

View File

@@ -0,0 +1,474 @@
/**
* Copyright (c) 2010-2020 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.neohub.internal;
import static org.openhab.binding.neohub.internal.NeoHubBindingConstants.*;
import java.io.IOException;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import javax.measure.Unit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.neohub.internal.NeoHubAbstractDeviceData.AbstractRecord;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseBridgeHandler;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.types.Command;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonSyntaxException;
import tec.uom.se.unit.Units;
/**
* The {@link NeoHubHandler} is the openHAB Handler for NeoHub devices
*
* @author Andrew Fiddian-Green - Initial contribution (v2.x binding code)
* @author Sebastian Prehn - Initial contribution (v1.x hub communication)
*
*/
@NonNullByDefault
public class NeoHubHandler extends BaseBridgeHandler {
private final Logger logger = LoggerFactory.getLogger(NeoHubHandler.class);
private final Map<String, Boolean> connectionStates = new HashMap<>();
private @Nullable NeoHubConfiguration config;
private @Nullable NeoHubSocket socket;
private @Nullable ScheduledFuture<?> lazyPollingScheduler;
private @Nullable ScheduledFuture<?> fastPollingScheduler;
private final AtomicInteger fastPollingCallsToGo = new AtomicInteger();
private @Nullable NeoHubReadDcbResponse systemData = null;
private boolean isLegacyApiSelected = true;
private boolean isApiOnline = false;
public NeoHubHandler(Bridge bridge) {
super(bridge);
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
// future: currently there is nothing to do for a NeoHub
}
@Override
public void initialize() {
NeoHubConfiguration config = getConfigAs(NeoHubConfiguration.class);
if (logger.isDebugEnabled()) {
logger.debug("hostname={}", config.hostName);
}
if (!MATCHER_IP_ADDRESS.matcher(config.hostName).matches()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "parameter hostName must be set!");
return;
}
if (logger.isDebugEnabled()) {
logger.debug("port={}", config.portNumber);
}
if (config.portNumber <= 0 || config.portNumber > 0xFFFF) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "portNumber is invalid!");
return;
}
if (logger.isDebugEnabled()) {
logger.debug("polling interval={}", config.pollingInterval);
}
if (config.pollingInterval < FAST_POLL_INTERVAL || config.pollingInterval > LAZY_POLL_INTERVAL) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, String
.format("pollingInterval must be in range [%d..%d]!", FAST_POLL_INTERVAL, LAZY_POLL_INTERVAL));
return;
}
if (logger.isDebugEnabled()) {
logger.debug("socketTimeout={}", config.socketTimeout);
}
if (config.socketTimeout < 5 || config.socketTimeout > 20) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
String.format("socketTimeout must be in range [%d..%d]!", 5, 20));
return;
}
if (logger.isDebugEnabled()) {
logger.debug("preferLegacyApi={}", config.preferLegacyApi);
}
socket = new NeoHubSocket(config.hostName, config.portNumber, config.socketTimeout);
this.config = config;
if (logger.isDebugEnabled()) {
logger.debug("start background polling..");
}
// create a "lazy" polling scheduler
ScheduledFuture<?> lazy = this.lazyPollingScheduler;
if (lazy == null || lazy.isCancelled()) {
this.lazyPollingScheduler = scheduler.scheduleWithFixedDelay(this::lazyPollingSchedulerExecute,
config.pollingInterval, config.pollingInterval, TimeUnit.SECONDS);
}
// create a "fast" polling scheduler
fastPollingCallsToGo.set(FAST_POLL_CYCLES);
ScheduledFuture<?> fast = this.fastPollingScheduler;
if (fast == null || fast.isCancelled()) {
this.fastPollingScheduler = scheduler.scheduleWithFixedDelay(this::fastPollingSchedulerExecute,
FAST_POLL_INTERVAL, FAST_POLL_INTERVAL, TimeUnit.SECONDS);
}
updateStatus(ThingStatus.UNKNOWN);
// start a fast polling burst to ensure the NeHub is initialized quickly
startFastPollingBurst();
}
@Override
public void dispose() {
if (logger.isDebugEnabled()) {
logger.debug("stop background polling..");
}
// clean up the lazy polling scheduler
ScheduledFuture<?> lazy = this.lazyPollingScheduler;
if (lazy != null && !lazy.isCancelled()) {
lazy.cancel(true);
this.lazyPollingScheduler = null;
}
// clean up the fast polling scheduler
ScheduledFuture<?> fast = this.fastPollingScheduler;
if (fast != null && !fast.isCancelled()) {
fast.cancel(true);
this.fastPollingScheduler = null;
}
}
/*
* device handlers call this to initiate a burst of fast polling requests (
* improves response time to users when openHAB changes a channel value )
*/
public void startFastPollingBurst() {
fastPollingCallsToGo.set(FAST_POLL_CYCLES);
}
/*
* device handlers call this method to issue commands to the NeoHub
*/
public synchronized NeoHubReturnResult toNeoHubSendChannelValue(String commandStr) {
NeoHubSocket socket = this.socket;
if (socket == null || config == null) {
return NeoHubReturnResult.ERR_INITIALIZATION;
}
try {
socket.sendMessage(commandStr);
// start a fast polling burst (to confirm the status change)
startFastPollingBurst();
return NeoHubReturnResult.SUCCEEDED;
} catch (Exception e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
logger.warn(MSG_FMT_SET_VALUE_ERR, commandStr, e.getMessage());
return NeoHubReturnResult.ERR_COMMUNICATION;
}
}
/**
* sends a JSON request to the NeoHub to read the device data
*
* @return a class that contains the full status of all devices
*/
protected @Nullable NeoHubAbstractDeviceData fromNeoHubGetDeviceData() {
NeoHubSocket socket = this.socket;
if (socket == null || config == null) {
logger.warn(MSG_HUB_CONFIG);
return null;
}
try {
String responseJson;
NeoHubAbstractDeviceData deviceData;
if (isLegacyApiSelected) {
responseJson = socket.sendMessage(CMD_CODE_INFO);
deviceData = NeoHubInfoResponse.createDeviceData(responseJson);
} else {
responseJson = socket.sendMessage(CMD_CODE_GET_LIVE_DATA);
deviceData = NeoHubLiveDeviceData.createDeviceData(responseJson);
}
if (deviceData == null) {
logger.warn(MSG_FMT_DEVICE_POLL_ERR, "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.size() == 0) {
logger.warn(MSG_FMT_DEVICE_POLL_ERR, "no devices found");
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
return null;
}
if (getThing().getStatus() != ThingStatus.ONLINE) {
updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE);
}
// check if we also need to discard and update systemData
NeoHubReadDcbResponse systemData = this.systemData;
if (systemData != null) {
if (deviceData instanceof NeoHubLiveDeviceData) {
/*
* note: time-stamps are measured in seconds from 1970-01-01T00:00:00Z
*
* new API: discard systemData if its time-stamp is older than the system
* time-stamp on the hub
*/
if (systemData.timeStamp < ((NeoHubLiveDeviceData) deviceData).getTimestampSystem()) {
this.systemData = null;
}
} else {
/*
* note: time-stamps are measured in seconds from 1970-01-01T00:00:00Z
*
* legacy API: discard systemData if its time-stamp is older than one hour
*/
if (systemData.timeStamp < Instant.now().minus(1, ChronoUnit.HOURS).getEpochSecond()) {
this.systemData = null;
}
}
}
return deviceData;
} catch (Exception e) {
logger.warn(MSG_FMT_DEVICE_POLL_ERR, e.getMessage());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
return null;
}
}
/**
* sends a JSON request to the NeoHub to read the system data
*
* @return a class that contains the status of the system
*/
protected @Nullable NeoHubReadDcbResponse fromNeoHubReadSystemData() {
NeoHubSocket socket = this.socket;
if (socket == null) {
return null;
}
try {
String responseJson;
NeoHubReadDcbResponse systemData;
if (isLegacyApiSelected) {
responseJson = socket.sendMessage(CMD_CODE_READ_DCB);
systemData = NeoHubReadDcbResponse.createSystemData(responseJson);
} else {
responseJson = socket.sendMessage(CMD_CODE_GET_SYSTEM);
systemData = NeoHubReadDcbResponse.createSystemData(responseJson);
}
if (systemData == null) {
logger.warn(MSG_FMT_SYSTEM_POLL_ERR, "failed to create system data response");
return null;
}
return systemData;
} catch (Exception e) {
logger.warn(MSG_FMT_SYSTEM_POLL_ERR, e.getMessage());
return null;
}
}
/*
* this is the callback used by the lazy polling scheduler.. fetches the info
* for all devices from the NeoHub, and passes the results the respective device
* handlers
*/
private synchronized void lazyPollingSchedulerExecute() {
// check which API is supported
if (!isApiOnline) {
selectApi();
}
NeoHubAbstractDeviceData deviceData = fromNeoHubGetDeviceData();
if (deviceData != null) {
// dispatch deviceData to each of the hub's owned devices ..
List<Thing> children = getThing().getThings();
for (Thing child : children) {
ThingHandler device = child.getHandler();
if (device instanceof NeoBaseHandler) {
((NeoBaseHandler) device).toBaseSendPollResponse(deviceData);
}
}
// evaluate and update the state of our RF mesh QoS channel
List<? extends AbstractRecord> devices = deviceData.getDevices();
State state;
if (devices == null || devices.isEmpty()) {
state = UnDefType.UNDEF;
} else {
int totalDeviceCount = devices.size();
int onlineDeviceCount = 0;
for (AbstractRecord device : devices) {
String deviceName = device.getDeviceName();
Boolean online = !device.offline();
@Nullable
Boolean onlineBefore = connectionStates.put(deviceName, online);
if (!online.equals(onlineBefore)) {
logger.info("device \"{}\" has {} the RF mesh network", deviceName,
online.booleanValue() ? "joined" : "left");
}
if (online.booleanValue()) {
onlineDeviceCount++;
}
}
state = new QuantityType<>((100.0 * onlineDeviceCount) / totalDeviceCount, Units.PERCENT);
}
updateState(CHAN_MESH_NETWORK_QOS, state);
}
if (fastPollingCallsToGo.get() > 0) {
fastPollingCallsToGo.decrementAndGet();
}
}
/*
* this is the callback used by the fast polling scheduler.. checks if a fast
* polling burst is scheduled, and if so calls lazyPollingSchedulerExecute
*/
private void fastPollingSchedulerExecute() {
if (fastPollingCallsToGo.get() > 0) {
lazyPollingSchedulerExecute();
}
}
/*
* select whether to use the old "deprecated" API or the new API
*/
private void selectApi() {
boolean supportsLegacyApi = false;
boolean supportsFutureApi = false;
NeoHubSocket socket = this.socket;
if (socket != null) {
String responseJson;
NeoHubReadDcbResponse systemData;
try {
responseJson = socket.sendMessage(CMD_CODE_READ_DCB);
systemData = NeoHubReadDcbResponse.createSystemData(responseJson);
supportsLegacyApi = systemData != null;
if (!supportsLegacyApi) {
throw new NeoHubException("legacy API not supported");
}
} catch (JsonSyntaxException | NeoHubException | IOException e) {
// we learned that this API is not currently supported; no big deal
logger.debug("Legacy API is not supported!");
}
try {
responseJson = socket.sendMessage(CMD_CODE_GET_SYSTEM);
systemData = NeoHubReadDcbResponse.createSystemData(responseJson);
supportsFutureApi = systemData != null;
if (!supportsFutureApi) {
throw new NeoHubException("new API not supported");
}
} catch (JsonSyntaxException | NeoHubException | IOException e) {
// we learned that this API is not currently supported; no big deal
logger.debug("New API is not supported!");
}
}
if (!supportsLegacyApi && !supportsFutureApi) {
logger.warn("Currently neither legacy nor new API are supported!");
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\"");
}
this.isLegacyApiSelected = isLegacyApiSelected;
this.isApiOnline = true;
}
/*
* get the Engineers data
*/
public @Nullable NeoHubGetEngineersData fromNeoHubGetEngineersData() {
NeoHubSocket socket = this.socket;
if (socket != null) {
String responseJson;
try {
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());
}
}
return null;
}
public boolean isLegacyApiSelected() {
return isLegacyApiSelected;
}
public Unit<?> getTemperatureUnit() {
NeoHubReadDcbResponse systemData = this.systemData;
if (systemData == null) {
this.systemData = systemData = fromNeoHubReadSystemData();
}
if (systemData != null) {
return systemData.getTemperatureUnit();
}
return SIUnits.CELSIUS;
}
}

View File

@@ -0,0 +1,137 @@
/**
* Copyright (c) 2010-2020 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.neohub.internal;
import static org.openhab.binding.neohub.internal.NeoHubBindingConstants.*;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Map;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.config.discovery.DiscoveryService;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.component.annotations.Component;
/**
* The {@link NeoHubHandlerFactory} creates things and thing handlers
*
* @author Andrew Fiddian-Green - Initial contribution
*/
@NonNullByDefault
@Component(configurationPid = "binding.neohub", service = ThingHandlerFactory.class)
public class NeoHubHandlerFactory extends BaseThingHandlerFactory {
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections
.unmodifiableSet(new HashSet<>(Arrays.asList(THING_TYPE_NEOHUB, THING_TYPE_NEOSTAT, THING_TYPE_NEOPLUG,
THING_TYPE_NEOCONTACT, THING_TYPE_NEOTEMPERATURESENSOR)));
private final Map<ThingUID, ServiceRegistration<?>> discoServices = new HashMap<>();
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
}
@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if ((thingTypeUID.equals(THING_TYPE_NEOHUB)) && (thing instanceof Bridge)) {
NeoHubHandler handler = new NeoHubHandler((Bridge) thing);
createDiscoveryService(handler);
return handler;
}
if (thingTypeUID.equals(THING_TYPE_NEOSTAT)) {
return new NeoStatHandler(thing);
}
if (thingTypeUID.equals(THING_TYPE_NEOPLUG)) {
return new NeoPlugHandler(thing);
}
if (thingTypeUID.equals(THING_TYPE_NEOCONTACT)) {
return new NeoContactHandler(thing);
}
if (thingTypeUID.equals(THING_TYPE_NEOTEMPERATURESENSOR)) {
return new NeoTemperatureSensorHandler(thing);
}
return null;
}
@Override
protected synchronized void removeHandler(ThingHandler handler) {
if (handler instanceof NeoHubHandler) {
destroyDiscoveryService((NeoHubHandler) handler);
}
}
/*
* create a discovery service so that a newly created hub will find the
* respective things tht are inside it
*/
private synchronized void createDiscoveryService(NeoHubHandler handler) {
// create a new discovery service
NeoHubDiscoveryService ds = new NeoHubDiscoveryService(handler);
// activate the discovery service
ds.activate();
// register the discovery service
ServiceRegistration<?> serviceReg = bundleContext.registerService(DiscoveryService.class.getName(), ds,
new Hashtable<>());
/*
* store service registration in a list so we can destroy it when the respective
* hub is destroyed
*/
discoServices.put(handler.getThing().getUID(), serviceReg);
}
/*
* destroy the discovery service
*/
@SuppressWarnings("null")
private synchronized void destroyDiscoveryService(NeoHubHandler handler) {
// fetch the respective thing's service registration from our list
ServiceRegistration<?> serviceReg = discoServices.remove(handler.getThing().getUID());
if (serviceReg != null) {
// retrieve the respective discovery service
NeoHubDiscoveryService disco = (NeoHubDiscoveryService) bundleContext.getService(serviceReg.getReference());
// and unregister the service
serviceReg.unregister();
// deactivate the service
if (disco != null) {
disco.deactivate();
}
}
}
}

View File

@@ -0,0 +1,208 @@
/**
* Copyright (c) 2010-2020 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.neohub.internal;
import java.math.BigDecimal;
import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;
import com.google.gson.annotations.SerializedName;
/**
* A wrapper around the JSON response to the JSON INFO request
*
* @author Sebastian Prehn - Initial contribution
* @author Andrew Fiddian-Green - Refactoring for openHAB v2.x
*
*/
@NonNullByDefault
public class NeoHubInfoResponse extends NeoHubAbstractDeviceData {
private static final Gson GSON = new GsonBuilder()
.registerTypeAdapter(NeohubBool.class, new NeohubBoolDeserializer()).create();
@SerializedName("devices")
private @Nullable List<InfoRecord> deviceRecords;
@SuppressWarnings("null")
@NonNullByDefault
static class StatMode {
@SerializedName("MANUAL_OFF")
private @Nullable NeohubBool manualOff;
@SerializedName("MANUAL_ON")
private @Nullable NeohubBool manualOn;
private boolean stateManualOn() {
NeohubBool manualOn = this.manualOn;
return (manualOn == null ? false : manualOn.value);
}
private boolean stateManualOff() {
NeohubBool manualOff = this.manualOff;
return (manualOff == null ? false : manualOff.value);
}
}
@SuppressWarnings("null")
@NonNullByDefault
public static class InfoRecord extends AbstractRecord {
@SerializedName("device")
private @Nullable String deviceName;
@SerializedName("CURRENT_SET_TEMPERATURE")
private @Nullable BigDecimal currentSetTemperature;
@SerializedName("CURRENT_TEMPERATURE")
private @Nullable BigDecimal currentTemperature;
@SerializedName("CURRENT_FLOOR_TEMPERATURE")
private @Nullable BigDecimal currentFloorTemperature;
@SerializedName("COOL_INP")
private @Nullable NeohubBool coolInput;
@SerializedName("LOW_BATTERY")
private @Nullable NeohubBool batteryLow;
@SerializedName("STANDBY")
private @Nullable NeohubBool standby;
@SerializedName("HEATING")
private @Nullable NeohubBool heating;
@SerializedName("PREHEAT")
private @Nullable NeohubBool preHeat;
@SerializedName("TIMER")
private @Nullable NeohubBool timerOn;
@SerializedName("DEVICE_TYPE")
private @Nullable BigDecimal deviceType;
@SerializedName("OFFLINE")
private @Nullable NeohubBool offline;
@SerializedName("STAT_MODE")
private @Nullable StatMode statMode = new StatMode();
private boolean safeBoolean(@Nullable NeohubBool value) {
return (value == null ? false : value.value);
}
@Override
public String getDeviceName() {
String deviceName = this.deviceName;
return deviceName != null ? deviceName : "";
}
@Override
public BigDecimal getTargetTemperature() {
return safeBigDecimal(currentSetTemperature);
}
@Override
public BigDecimal getActualTemperature() {
return safeBigDecimal(currentTemperature);
}
@Override
public BigDecimal getFloorTemperature() {
return safeBigDecimal(currentFloorTemperature);
}
@Override
public boolean isStandby() {
return safeBoolean(standby);
}
@Override
public boolean isHeating() {
return safeBoolean(heating);
}
@Override
public boolean isPreHeating() {
return safeBoolean(preHeat);
}
@Override
public boolean isTimerOn() {
return safeBoolean(timerOn);
}
@Override
public boolean offline() {
return safeBoolean(offline);
}
@Override
public boolean stateManual() {
StatMode statMode = this.statMode;
return (statMode != null && statMode.stateManualOn());
}
@Override
public boolean stateAuto() {
StatMode statMode = this.statMode;
return (statMode != null && statMode.stateManualOff());
}
@Override
public boolean isWindowOpen() {
// legacy API misuses the cool input parameter
return safeBoolean(coolInput);
}
@Override
public boolean isBatteryLow() {
return safeBoolean(batteryLow);
}
public int getDeviceType() {
BigDecimal deviceType = this.deviceType;
return deviceType != null ? deviceType.intValue() : -1;
}
}
/**
* Create wrapper around a JSON string
*
* @param fromJson the JSON string
* @return a NeoHubInfoResponse wrapper around the JSON
* @throws JsonSyntaxException
*
*/
public static @Nullable NeoHubInfoResponse createDeviceData(String fromJson) throws JsonSyntaxException {
return GSON.fromJson(fromJson, NeoHubInfoResponse.class);
}
/**
* returns the device record corresponding to a given device name
*
* @param deviceName the device name
* @return its respective device record
*/
@Override
public @Nullable AbstractRecord getDeviceRecord(String deviceName) {
List<InfoRecord> deviceRecords = this.deviceRecords;
if (deviceRecords != null) {
for (AbstractRecord deviceRecord : deviceRecords) {
if (deviceName.equals(deviceRecord.getDeviceName())) {
return deviceRecord;
}
}
}
return null;
}
/**
* @return the full list of device records
*/
@Override
public @Nullable List<InfoRecord> getDevices() {
return deviceRecords;
}
}

View File

@@ -0,0 +1,194 @@
/**
* Copyright (c) 2010-2020 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.neohub.internal;
import java.math.BigDecimal;
import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.google.gson.annotations.SerializedName;
/**
* A wrapper around the JSON response to the JSON GET_LIVE_DATA request
*
* @author Andrew Fiddian-Green - Initial contribution
*/
@NonNullByDefault
public class NeoHubLiveDeviceData extends NeoHubAbstractDeviceData {
private static final Gson GSON = new Gson();
@SerializedName("TIMESTAMP_ENGINEERS")
private @Nullable BigDecimal timestampEngineers;
@SerializedName("TIMESTAMP_SYSTEM")
private @Nullable BigDecimal timestampSystem;
@SerializedName("devices")
private @Nullable List<LiveDataRecord> deviceRecords;
@SuppressWarnings("null")
@NonNullByDefault
public static class LiveDataRecord extends AbstractRecord {
// "alternate" is a special kludge for technical devices
@SerializedName(value = "ZONE_NAME", alternate = { "device" })
private @Nullable String deviceName;
@SerializedName("SET_TEMP")
private @Nullable BigDecimal currentSetTemperature;
@SerializedName("ACTUAL_TEMP")
private @Nullable BigDecimal currentTemperature;
@SerializedName("CURRENT_FLOOR_TEMPERATURE")
private @Nullable BigDecimal currentFloorTemperature;
@SerializedName("WINDOW_OPEN")
private @Nullable Boolean windowOpen;
@SerializedName("LOW_BATTERY")
private @Nullable Boolean batteryLow;
@SerializedName("STANDBY")
private @Nullable Boolean standby;
@SerializedName("HEAT_ON")
private @Nullable Boolean heating;
@SerializedName("PREHEAT_ACTIVE")
private @Nullable Boolean preHeat;
@SerializedName("TIMER_ON")
private @Nullable Boolean timerOn;
@SerializedName("OFFLINE")
private @Nullable Boolean offline;
@SerializedName("MANUAL_OFF")
private @Nullable Boolean manualOff;
@SerializedName("MANUAL_ON")
private @Nullable Boolean manualOn;
private boolean safeBoolean(@Nullable Boolean value) {
return (value == null ? false : value.booleanValue());
}
@Override
public String getDeviceName() {
String deviceName = this.deviceName;
return deviceName != null ? deviceName : "";
}
@Override
public BigDecimal getTargetTemperature() {
return safeBigDecimal(currentSetTemperature);
}
@Override
public BigDecimal getActualTemperature() {
return safeBigDecimal(currentTemperature);
}
@Override
public BigDecimal getFloorTemperature() {
return safeBigDecimal(currentFloorTemperature);
}
@Override
public boolean isStandby() {
return safeBoolean(standby);
}
@Override
public boolean isHeating() {
return safeBoolean(heating);
}
@Override
public boolean isPreHeating() {
return safeBoolean(preHeat);
}
@Override
public boolean isTimerOn() {
return safeBoolean(timerOn);
}
@Override
public boolean offline() {
return safeBoolean(offline);
}
@Override
public boolean stateManual() {
return safeBoolean(manualOn);
}
@Override
public boolean stateAuto() {
return safeBoolean(manualOff);
}
@Override
public boolean isWindowOpen() {
return safeBoolean(windowOpen);
}
@Override
public boolean isBatteryLow() {
return safeBoolean(batteryLow);
}
}
public long getTimestampEngineers() {
BigDecimal timestampEngineers = this.timestampEngineers;
return timestampEngineers != null ? timestampEngineers.longValue() : 0;
}
public long getTimestampSystem() {
BigDecimal timestampSystem = this.timestampSystem;
return timestampSystem != null ? timestampSystem.longValue() : 0;
}
/**
* Create wrapper around a JSON string
*
* @param fromJson the JSON string
* @return a NeoHubGetLiveDataResponse wrapper around the JSON string
* @throws JsonSyntaxException
*
*/
public static @Nullable NeoHubLiveDeviceData createDeviceData(String fromJson) throws JsonSyntaxException {
return GSON.fromJson(fromJson, NeoHubLiveDeviceData.class);
}
/**
* returns the device record corresponding to a given device name
*
* @param deviceName the device name
* @return its respective device record
*/
@Override
public @Nullable AbstractRecord getDeviceRecord(String deviceName) {
List<LiveDataRecord> deviceRecords = this.deviceRecords;
if (deviceRecords != null) {
for (AbstractRecord deviceRecord : deviceRecords) {
if (deviceName.equals(deviceRecord.getDeviceName())) {
return deviceRecord;
}
}
}
return null;
}
/**
* @return the full list of device records
*/
@Override
public @Nullable List<LiveDataRecord> getDevices() {
return deviceRecords;
}
}

View File

@@ -0,0 +1,65 @@
/**
* Copyright (c) 2010-2020 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.neohub.internal;
import java.time.Instant;
import javax.measure.Unit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.library.unit.ImperialUnits;
import org.openhab.core.library.unit.SIUnits;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.google.gson.annotations.SerializedName;
/**
* A wrapper around the JSON response to the JSON READ_DCB and GET_SYSTEM
* request
*
* @author Andrew Fiddian-Green - Initial contribution
*/
@NonNullByDefault
public class NeoHubReadDcbResponse {
private static final Gson GSON = new Gson();
@SerializedName("CORF")
private @Nullable String degreesCorF;
/*
* note: time-stamps are measured in seconds from 1970-01-01T00:00:00Z
*
* this time-stamp is the moment of creation of this class instance; it is used
* to compare with the system last change time-stamp reported by the hub
*/
public final long timeStamp = Instant.now().getEpochSecond();
public Unit<?> getTemperatureUnit() {
return "F".equalsIgnoreCase(degreesCorF) ? ImperialUnits.FAHRENHEIT : SIUnits.CELSIUS;
}
/**
* Create wrapper around a JSON string
*
* @param fromJson the JSON string
* @return a NeoHubReadDcbResponse wrapper around the JSON string
* @throws JsonSyntaxException
*
*/
public static @Nullable NeoHubReadDcbResponse createSystemData(String fromJson) throws JsonSyntaxException {
return GSON.fromJson(fromJson, NeoHubReadDcbResponse.class);
}
}

View File

@@ -0,0 +1,126 @@
/**
* Copyright (c) 2010-2020 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.neohub.internal;
import static java.nio.charset.StandardCharsets.US_ASCII;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.InetSocketAddress;
import java.net.Socket;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* NeoHubConnector handles the ASCII based communication via TCP between openHAB
* and NeoHub
*
* @author Sebastian Prehn - Initial contribution
* @author Andrew Fiddian-Green - Refactoring for openHAB v2.x
*
*/
@NonNullByDefault
public class NeoHubSocket {
private final Logger logger = LoggerFactory.getLogger(NeoHubSocket.class);
/**
* Name of host or IP to connect to.
*/
private final String hostname;
/**
* The port to connect to
*/
private final int port;
/**
* The socket connect resp. read timeout value
*/
private final int timeout;
public NeoHubSocket(final String hostname, final int portNumber, final int timeoutSeconds) {
this.hostname = hostname;
this.port = portNumber;
this.timeout = timeoutSeconds * 1000;
}
/**
* sends the message over the network to the NeoHub and returns its response
*
* @param requestJson the message to be sent to the NeoHub
* @return responseJson received from NeoHub
* @throws NeoHubException, IOException
*
*/
public String sendMessage(final String requestJson) throws IOException, NeoHubException {
IOException caughtException = null;
StringBuilder builder = new StringBuilder();
try (Socket socket = new Socket()) {
socket.connect(new InetSocketAddress(hostname, port), timeout);
socket.setSoTimeout(timeout);
try (InputStreamReader reader = new InputStreamReader(socket.getInputStream(), US_ASCII);
OutputStreamWriter writer = new OutputStreamWriter(socket.getOutputStream(), US_ASCII)) {
if (logger.isDebugEnabled()) {
logger.debug("sending {} characters..", requestJson.length());
logger.debug(">> {}", requestJson);
}
writer.write(requestJson);
writer.write(0); // NULL terminate the command string
writer.flush();
if (logger.isTraceEnabled()) {
logger.trace("sent {} characters..", requestJson.length());
}
int inChar;
// NULL termination, end of stream (-1), or newline
while (((inChar = reader.read()) > 0) && (inChar != '\n')) {
builder.append((char) inChar);
}
}
} catch (IOException e) {
// catch IOExceptions here, and save them to be re-thrown later
caughtException = e;
}
String responseJson = builder.toString();
if (logger.isTraceEnabled()) {
logger.trace("received {} characters..", responseJson.length());
logger.trace("<< {}", responseJson);
} else
if (logger.isDebugEnabled()) {
logger.debug("received {} characters (set log level to TRACE to see full string)..", responseJson.length());
logger.debug("<< {} ...", responseJson.substring(0, Math.min(responseJson.length(), 30)));
}
// if any type of Exception was caught above, re-throw it again to the caller
if (caughtException != null) {
throw caughtException;
}
if (responseJson.isEmpty()) {
throw new NeoHubException("empty response string");
}
return responseJson;
}
}

View File

@@ -0,0 +1,68 @@
/**
* Copyright (c) 2010-2020 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.neohub.internal;
import static org.openhab.binding.neohub.internal.NeoHubBindingConstants.*;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.Thing;
import org.openhab.core.types.Command;
/**
* The {@link NeoPlugHandler} is the OpenHAB Handler for NeoPlug devices Note:
* inherits almost all the functionality of a {@link NeoBaseHandler}
*
* @author Andrew Fiddian-Green - Initial contribution
*
*/
@NonNullByDefault
public class NeoPlugHandler extends NeoBaseHandler {
public NeoPlugHandler(Thing thing) {
super(thing);
}
// =========== methods of NeoBaseHandler that are overridden ================
@Override
protected String toNeoHubBuildCommandString(String channelId, Command command) {
NeoBaseConfiguration config = this.config;
if (config != null) {
if (command instanceof OnOffType && channelId.equals(CHAN_PLUG_OUTPUT_STATE)) {
return String.format(CMD_CODE_TIMER, ((OnOffType) command).toString(), config.deviceNameInHub);
}
if (command instanceof OnOffType && channelId.equals(CHAN_PLUG_AUTO_MODE)) {
return String.format(CMD_CODE_MANUAL, invert((OnOffType) command).toString(), config.deviceNameInHub);
}
}
return "";
}
@Override
protected void toNeoHubSendCommandSet(String channelId, Command command) {
// if this is a manual command, switch to manual mode first..
if (channelId.equals(CHAN_PLUG_OUTPUT_STATE) && command instanceof OnOffType) {
toNeoHubSendCommand(CHAN_PLUG_AUTO_MODE, OnOffType.from(false));
}
// send the actual command to the hub
toNeoHubSendCommand(channelId, command);
}
@Override
protected void toOpenHabSendChannelValues(NeoHubAbstractDeviceData.AbstractRecord deviceRecord) {
boolean offline = deviceRecord.offline();
toOpenHabSendValueDebounced(CHAN_PLUG_AUTO_MODE, OnOffType.from(!deviceRecord.stateManual()), offline);
toOpenHabSendValueDebounced(CHAN_PLUG_OUTPUT_STATE, OnOffType.from(deviceRecord.isTimerOn()), offline);
}
}

View File

@@ -0,0 +1,84 @@
/**
* Copyright (c) 2010-2020 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.neohub.internal;
import static org.openhab.binding.neohub.internal.NeoHubBindingConstants.*;
import javax.measure.Unit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.Thing;
import org.openhab.core.types.Command;
/**
* The {@link NeoStatHandler} is the openHAB Handler for NeoStat devices Note:
* inherits almost all the functionality of a {@link NeoBaseHandler}
*
* @author Andrew Fiddian-Green - Initial contribution
*
*/
@NonNullByDefault
public class NeoStatHandler extends NeoBaseHandler {
public NeoStatHandler(Thing thing) {
super(thing);
}
@Override
protected String toNeoHubBuildCommandString(String channelId, Command command) {
NeoBaseConfiguration config = this.config;
if (config != null) {
if (command instanceof QuantityType<?> && channelId.equals(CHAN_TARGET_TEMP)) {
Command doCommand = command;
QuantityType<?> temp = ((QuantityType<?>) command).toUnit(getTemperatureUnit());
if (temp != null) {
doCommand = temp;
}
return String.format(CMD_CODE_TEMP, ((QuantityType<?>) doCommand).toBigDecimal().toString(),
config.deviceNameInHub);
}
if (command instanceof OnOffType && channelId.equals(CHAN_OCC_MODE_PRESENT)) {
return String.format(CMD_CODE_AWAY, invert((OnOffType) command).toString(), config.deviceNameInHub);
}
}
return "";
}
@Override
protected void toOpenHabSendChannelValues(NeoHubAbstractDeviceData.AbstractRecord deviceRecord) {
Unit<?> unit = getTemperatureUnit();
boolean offline = deviceRecord.offline();
toOpenHabSendValueDebounced(CHAN_TARGET_TEMP, new QuantityType<>(deviceRecord.getTargetTemperature(), unit),
offline);
toOpenHabSendValueDebounced(CHAN_ROOM_TEMP, new QuantityType<>(deviceRecord.getActualTemperature(), unit),
offline);
toOpenHabSendValueDebounced(CHAN_FLOOR_TEMP, new QuantityType<>(deviceRecord.getFloorTemperature(), unit),
offline);
toOpenHabSendValueDebounced(CHAN_OCC_MODE_PRESENT, OnOffType.from(!deviceRecord.isStandby()), offline);
toOpenHabSendValueDebounced(CHAN_STAT_OUTPUT_STATE,
(deviceRecord.isHeating() || deviceRecord.isPreHeating() ? new StringType(VAL_HEATING)
: new StringType(VAL_OFF)),
offline);
toOpenHabSendValueDebounced(CHAN_BATTERY_LOW_ALARM, OnOffType.from(deviceRecord.isBatteryLow()), offline);
}
}

View File

@@ -0,0 +1,48 @@
/**
* Copyright (c) 2010-2020 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.neohub.internal;
import static org.openhab.binding.neohub.internal.NeoHubBindingConstants.*;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.thing.Thing;
/**
* The {@link NeoTemperatureSensorHandler} is the OpenHAB Handler for NeoTemperatureSensor devices
*
* Note: inherits almost all the functionality of a {@link NeoBaseHandler}
*
* @author Andrew Fiddian-Green - Initial contribution
*
*/
@NonNullByDefault
public class NeoTemperatureSensorHandler extends NeoBaseHandler {
public NeoTemperatureSensorHandler(Thing thing) {
super(thing);
}
// =========== methods of NeoBaseHandler that are overridden ================
@Override
protected void toOpenHabSendChannelValues(NeoHubAbstractDeviceData.AbstractRecord deviceRecord) {
boolean offline = deviceRecord.offline();
toOpenHabSendValueDebounced(CHAN_TEMPERATURE_SENSOR,
new QuantityType<>(deviceRecord.getActualTemperature(), getTemperatureUnit()), offline);
toOpenHabSendValueDebounced(CHAN_BATTERY_LOW_ALARM, OnOffType.from(deviceRecord.isBatteryLow()), offline);
}
}

View File

@@ -0,0 +1,32 @@
/**
* Copyright (c) 2010-2020 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.neohub.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* A custom class that wraps a boolean; we need this because newer versions of
* NeoHub have broken JSON for some boolean values so we can't use the standard
* deserializer, and thus have to use a custom one
*
* @author Andrew Fiddian-Green - Initial contribution
*
*/
@NonNullByDefault
public class NeohubBool {
public boolean value;
public NeohubBool(boolean value) {
this.value = value;
}
}

View File

@@ -0,0 +1,50 @@
/**
* Copyright (c) 2010-2020 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.neohub.internal;
import java.lang.reflect.Type;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
/**
* A custom deserializer for NeohubBool; we need this because newer versions of
* NeoHub have broken JSON for some boolean values so we can't use the standard
* deserializer, and thus have to use a custom one
*
* @author Andrew Fiddian-Green - Initial contribution
*
*/
@NonNullByDefault
public class NeohubBoolDeserializer implements JsonDeserializer<NeohubBool> {
@Override
public NeohubBool deserialize(@Nullable JsonElement json, @Nullable Type typeOfT,
@Nullable JsonDeserializationContext context) throws JsonParseException {
if (json != null) {
JsonPrimitive jsonPrimitive = json.getAsJsonPrimitive();
if (jsonPrimitive.isBoolean()) {
return new NeohubBool(jsonPrimitive.getAsBoolean());
} else if (jsonPrimitive.isNumber()) {
return new NeohubBool(jsonPrimitive.getAsNumber().intValue() != 0);
}
}
return new NeohubBool(false);
}
}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<binding:binding id="neohub" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
<name>NeoHub Binding</name>
<description>This is the binding for Heatmiser NeoHub devices</description>
<author>Andrew Fiddian-Green</author>
</binding:binding>

View File

@@ -0,0 +1,310 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="neohub"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<bridge-type id="neohub">
<label>NeoHub</label>
<description>Heatmiser NeoHub bridge to NeoStat and NeoPlug devices</description>
<channels>
<channel typeId="meshNetworkQoS" id="meshNetworkQoS"/>
</channels>
<properties>
<property name="vendor">Heatmiser</property>
<property name="modelId">NeoHub</property>
</properties>
<config-description>
<parameter name="hostName" type="text" required="true">
<label>Host Name</label>
<context>network-address</context>
<description>Host name (IP address) of the NeoHub</description>
</parameter>
<parameter name="portNumber" type="integer" required="false">
<label>Port Number</label>
<description>Port number of the NeoHub</description>
<default>4242</default>
<advanced>true</advanced>
</parameter>
<parameter name="pollingInterval" type="integer" min="4" max="60" required="false" unit="s">
<label>Polling Interval</label>
<description>Time (seconds) between polling the NeoHub (min=4, max/default=60)</description>
<default>60</default>
<advanced>true</advanced>
</parameter>
<parameter name="socketTimeout" type="integer" min="5" max="20" required="false" unit="s">
<label>Socket Timeout</label>
<description>Time (seconds) to wait for connections to the Hub (min/default=5, max=20)</description>
<default>5</default>
<advanced>true</advanced>
</parameter>
<parameter name="preferLegacyApi" type="boolean" required="false">
<label>Prefer Legacy API</label>
<description>Use the legacy API instead of the new API (if available)</description>
<default>false</default>
<advanced>true</advanced>
</parameter>
</config-description>
</bridge-type>
<thing-type id="neostat">
<supported-bridge-type-refs>
<bridge-type-ref id="neohub"/>
</supported-bridge-type-refs>
<label>Heatmiser NeoStat</label>
<description>Heatmiser Neo Smart Thermostat</description>
<channels>
<channel id="roomTemperature" typeId="temperature">
<label>Room Temperature</label>
<description>Actual room temperature</description>
</channel>
<channel id="targetTemperature" typeId="targetTemperature"/>
<channel id="floorTemperature" typeId="temperature">
<label>Floor Temperature</label>
<description>Actual floor temperature</description>
</channel>
<channel id="thermostatOutputState" typeId="thermostatOutputState"/>
<channel id="occupancyModePresent" typeId="occupancyModePresent"/>
<channel id="batteryLowAlarm" typeId="system.low-battery"/>
</channels>
<properties>
<property name="vendor">Heatmiser</property>
<property name="modelId">NeoStat</property>
</properties>
<representation-property>deviceNameInHub</representation-property>
<config-description>
<parameter name="deviceNameInHub" type="text" required="true">
<label>Device Name</label>
<description>Device Name that identifies the NeoStat device in the NeoHub and Heatmiser App</description>
</parameter>
</config-description>
</thing-type>
<thing-type id="neoplug">
<supported-bridge-type-refs>
<bridge-type-ref id="neohub"/>
</supported-bridge-type-refs>
<label>Heatmiser NeoPlug</label>
<description>Heatmiser Neo Smart Plug</description>
<channels>
<channel id="plugOutputState" typeId="plugOutputState"/>
<channel id="plugAutoMode" typeId="plugAutoMode"/>
</channels>
<properties>
<property name="vendor">Heatmiser</property>
<property name="modelId">NeoPlug</property>
</properties>
<representation-property>deviceNameInHub</representation-property>
<config-description>
<parameter name="deviceNameInHub" type="text" required="true">
<label>Device Name</label>
<description>Device Name that identifies the NeoPlug device in the NeoHub and Heatmiser App</description>
</parameter>
</config-description>
</thing-type>
<thing-type id="neocontact">
<supported-bridge-type-refs>
<bridge-type-ref id="neohub"/>
</supported-bridge-type-refs>
<label>Heatmiser Contact Sensor</label>
<description>Heatmiser (wireless) Window or Door Contact</description>
<channels>
<channel id="contactState" typeId="contactState"/>
<channel id="batteryLowAlarm" typeId="system.low-battery">
<label>Battery Low Alarm</label>
<description>ON if the device has a low battery</description>
</channel>
</channels>
<properties>
<property name="vendor">Heatmiser</property>
<property name="modelId">Contact Sensor</property>
</properties>
<representation-property>deviceNameInHub</representation-property>
<config-description>
<parameter name="deviceNameInHub" type="text" required="true">
<label>Device Name</label>
<description>Device Name that identifies the Contact in the NeoHub and Heatmiser App</description>
</parameter>
</config-description>
</thing-type>
<thing-type id="neotemperaturesensor">
<supported-bridge-type-refs>
<bridge-type-ref id="neohub"/>
</supported-bridge-type-refs>
<label>Heatmiser Wireless Air Sensor</label>
<description>Heatmiser (wireless) Temperature Sensor</description>
<channels>
<channel id="sensorTemperature" typeId="temperature">
<label>Temperature</label>
<description>Measured temperature value (Read-Only)</description>
</channel>
<channel id="batteryLowAlarm" typeId="system.low-battery">
<label>Battery Low Alarm</label>
<description>ON if the device has a low battery</description>
</channel>
</channels>
<properties>
<property name="vendor">Heatmiser</property>
<property name="modelId">Wireless Air Sensor</property>
</properties>
<representation-property>deviceNameInHub</representation-property>
<config-description>
<parameter name="deviceNameInHub" type="text" required="true">
<label>Device Name</label>
<description>Device Name that identifies the Temperature Sensor in the NeoHub and Heatmiser App</description>
</parameter>
</config-description>
</thing-type>
<channel-type id="temperature">
<item-type>Number:Temperature</item-type>
<label>Temperature</label>
<description>Measured temperature value (Read-Only)</description>
<category>temperature</category>
<state readOnly="true" pattern="%.1f %unit%"/>
<config-description>
<parameter name="holdOnlineState" type="boolean" required="false">
<label>Hold Online State</label>
<description>If the device loses its RF mesh connection, hold the last known state display value</description>
<default>false</default>
</parameter>
</config-description>
</channel-type>
<channel-type id="targetTemperature">
<item-type>Number:Temperature</item-type>
<label>Target Temperature</label>
<description>Target temperature setting of the room</description>
<category>temperature</category>
<state readOnly="false" pattern="%.1f %unit%" step="1"/>
<config-description>
<parameter name="holdOnlineState" type="boolean" required="false">
<label>Hold Online State</label>
<description>If the device loses its RF mesh connection, hold the last known state display value</description>
<default>false</default>
</parameter>
</config-description>
</channel-type>
<channel-type id="thermostatOutputState">
<item-type>String</item-type>
<label>Thermostat Output State</label>
<description>Status of whether the thermostat is Off, or calling for Heat</description>
<category>fire</category>
<state readOnly="true"/>
<config-description>
<parameter name="holdOnlineState" type="boolean" required="false">
<label>Hold Online State</label>
<description>If the device loses its RF mesh connection, hold the last known state display value</description>
<default>false</default>
</parameter>
</config-description>
</channel-type>
<channel-type id="occupancyModePresent">
<item-type>Switch</item-type>
<label>Occupancy Mode Present</label>
<description>The Thermostat is in the Present Occupancy Mode (Off=Absent, On=Present)</description>
<category>presence</category>
<state readOnly="false"/>
<config-description>
<parameter name="holdOnlineState" type="boolean" required="false">
<label>Hold Online State</label>
<description>If the device loses its RF mesh connection, hold the last known state display value</description>
<default>false</default>
</parameter>
</config-description>
</channel-type>
<channel-type id="plugAutoMode">
<item-type>Switch</item-type>
<label>Plug Auto Mode</label>
<description>The Plug is in Automatic Mode (Off=Manual, On=Automatic)</description>
<category>energy</category>
<state readOnly="false"/>
<config-description>
<parameter name="holdOnlineState" type="boolean" required="false">
<label>Hold Online State</label>
<description>If the device loses its RF mesh connection, hold the last known state display value</description>
<default>false</default>
</parameter>
</config-description>
</channel-type>
<channel-type id="plugOutputState">
<item-type>Switch</item-type>
<label>Plug Output State</label>
<description>The state of the Plug switch, Off or On</description>
<state readOnly="false"/>
<config-description>
<parameter name="holdOnlineState" type="boolean" required="false">
<label>Hold Online State</label>
<description>If the device loses its RF mesh connection, hold the last known state display value</description>
<default>false</default>
</parameter>
</config-description>
</channel-type>
<channel-type id="contactState">
<item-type>Contact</item-type>
<label>Contact State</label>
<description>The state of the contact</description>
<category>window</category>
<state readOnly="true"/>
<config-description>
<parameter name="holdOnlineState" type="boolean" required="false">
<label>Hold Online State</label>
<description>If the device loses its RF mesh connection, hold the last known state display value</description>
<default>false</default>
</parameter>
</config-description>
</channel-type>
<channel-type id="meshNetworkQoS">
<item-type>Number:Dimensionless</item-type>
<label>Mesh Network QoS</label>
<description>Quality of Service: percentage of configured devices currently connected to the RF mesh network</description>
<state readOnly="true" pattern="%.0f %%"></state>
</channel-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,399 @@
/**
* Copyright (c) 2010-2020 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.neohub.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.openhab.binding.neohub.internal.NeoHubBindingConstants.*;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.Instant;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.Test;
import org.openhab.binding.neohub.internal.NeoHubAbstractDeviceData;
import org.openhab.binding.neohub.internal.NeoHubAbstractDeviceData.AbstractRecord;
import org.openhab.binding.neohub.internal.NeoHubGetEngineersData;
import org.openhab.binding.neohub.internal.NeoHubInfoResponse;
import org.openhab.binding.neohub.internal.NeoHubInfoResponse.InfoRecord;
import org.openhab.binding.neohub.internal.NeoHubLiveDeviceData;
import org.openhab.binding.neohub.internal.NeoHubReadDcbResponse;
import org.openhab.binding.neohub.internal.NeoHubSocket;
import org.openhab.core.library.unit.ImperialUnits;
import org.openhab.core.library.unit.SIUnits;
/**
* The {@link NeoHubTestData} class defines common constants, which are used
* across the whole binding.
*
* @author Andrew Fiddian-Green - Initial contribution
*/
@NonNullByDefault
public class NeoHubTestData {
/*
* Load the test JSON payload string from a file
*/
private String load(String fileName) {
try (FileReader file = new FileReader(String.format("src/test/resources/%s.json", fileName));
BufferedReader reader = new BufferedReader(file)) {
StringBuilder builder = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
builder.append(line).append("\n");
}
return builder.toString();
} catch (IOException e) {
fail(e.getMessage());
}
return "";
}
/*
* Test an INFO JSON response string as produced by older firmware versions
*/
@Test
public void testInfoJsonOld() {
// load INFO JSON response string in old JSON format
NeoHubAbstractDeviceData infoResponse = NeoHubInfoResponse.createDeviceData(load("info_old"));
assertNotNull(infoResponse);
// missing device
AbstractRecord device = infoResponse.getDeviceRecord("Aardvark");
assertNull(device);
// existing type 12 thermostat device
device = infoResponse.getDeviceRecord("Dining Room");
assertNotNull(device);
assertEquals("Dining Room", device.getDeviceName());
assertEquals(new BigDecimal("22.0"), device.getTargetTemperature());
assertEquals(new BigDecimal("22.2"), device.getActualTemperature());
assertEquals(new BigDecimal("23"), device.getFloorTemperature());
assertTrue(device instanceof InfoRecord);
assertEquals(12, ((InfoRecord) device).getDeviceType());
assertFalse(device.isStandby());
assertFalse(device.isHeating());
assertFalse(device.isPreHeating());
assertFalse(device.isTimerOn());
assertFalse(device.offline());
assertFalse(device.stateManual());
assertTrue(device.stateAuto());
assertFalse(device.isWindowOpen());
assertFalse(device.isBatteryLow());
// existing type 6 plug device (MANUAL OFF)
device = infoResponse.getDeviceRecord("Plug South");
assertNotNull(device);
assertEquals("Plug South", device.getDeviceName());
assertTrue(device instanceof InfoRecord);
assertEquals(6, ((InfoRecord) device).getDeviceType());
assertFalse(device.isTimerOn());
assertTrue(device.stateManual());
// existing type 6 plug device (MANUAL ON)
device = infoResponse.getDeviceRecord("Plug North");
assertNotNull(device);
assertEquals("Plug North", device.getDeviceName());
assertTrue(device instanceof InfoRecord);
assertEquals(6, ((InfoRecord) device).getDeviceType());
assertTrue(device.isTimerOn());
assertTrue(device.stateManual());
// existing type 6 plug device (AUTO OFF)
device = infoResponse.getDeviceRecord("Watering System");
assertNotNull(device);
assertEquals("Watering System", device.getDeviceName());
assertTrue(device instanceof InfoRecord);
assertEquals(6, ((InfoRecord) device).getDeviceType());
assertFalse(device.isTimerOn());
assertFalse(device.stateManual());
}
/*
* Test an INFO JSON response string as produced by newer firmware versions
*/
@Test
public void testInfoJsonNew() {
// load INFO JSON response string in new JSON format
NeoHubAbstractDeviceData infoResponse = NeoHubInfoResponse.createDeviceData(load("info_new"));
assertNotNull(infoResponse);
// existing device (new JSON format)
AbstractRecord device = infoResponse.getDeviceRecord("Dining Room");
assertNotNull(device);
assertEquals("Dining Room", device.getDeviceName());
assertFalse(device.offline());
assertFalse(device.isWindowOpen());
// existing repeater device
device = infoResponse.getDeviceRecord("repeaternode54473");
assertNotNull(device);
assertEquals("repeaternode54473", device.getDeviceName());
assertEquals(new BigDecimal("127"), device.getFloorTemperature());
assertEquals(new BigDecimal("255.255"), device.getActualTemperature());
}
/*
* Test for a READ_DCB JSON string that has valid CORF C response
*/
@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());
// load READ_DCB JSON response string with valid CORF F response
dcbResponse = NeoHubReadDcbResponse.createSystemData(load("dcb_fahrenheit"));
assertNotNull(dcbResponse);
assertEquals(ImperialUnits.FAHRENHEIT, dcbResponse.getTemperatureUnit());
// load READ_DCB JSON response string with missing CORF element
dcbResponse = NeoHubReadDcbResponse.createSystemData(load("dcb_corf_missing"));
assertNotNull(dcbResponse);
assertEquals(SIUnits.CELSIUS, dcbResponse.getTemperatureUnit());
// load READ_DCB JSON response string where CORF element is an empty string
dcbResponse = NeoHubReadDcbResponse.createSystemData(load("dcb_corf_empty"));
assertNotNull(dcbResponse);
assertEquals(SIUnits.CELSIUS, dcbResponse.getTemperatureUnit());
}
/*
* Test an INFO JSON string that has a door contact and a temperature sensor
*/
@Test
public void testInfoJsonWithSensors() {
/*
* load an INFO JSON response string that has a closed door contact and a
* temperature sensor
*/
// save("info_sensors_closed", NEOHUB_JSON_TEST_STRING_INFO_SENSORS_CLOSED);
NeoHubAbstractDeviceData infoResponse = NeoHubInfoResponse.createDeviceData(load("info_sensors_closed"));
assertNotNull(infoResponse);
// existing contact device type 5 (CLOSED)
AbstractRecord device = infoResponse.getDeviceRecord("Back Door");
assertNotNull(device);
assertEquals("Back Door", device.getDeviceName());
assertTrue(device instanceof InfoRecord);
assertEquals(5, ((InfoRecord) device).getDeviceType());
assertFalse(device.isWindowOpen());
assertFalse(device.isBatteryLow());
// existing temperature sensor type 14
device = infoResponse.getDeviceRecord("Master Bedroom");
assertNotNull(device);
assertEquals("Master Bedroom", device.getDeviceName());
assertTrue(device instanceof InfoRecord);
assertEquals(14, ((InfoRecord) device).getDeviceType());
assertEquals(new BigDecimal("19.5"), device.getActualTemperature());
// existing thermostat type 1
device = infoResponse.getDeviceRecord("Living Room Floor");
assertNotNull(device);
assertEquals("Living Room Floor", device.getDeviceName());
assertTrue(device instanceof InfoRecord);
assertEquals(1, ((InfoRecord) device).getDeviceType());
assertEquals(new BigDecimal("19.8"), device.getActualTemperature());
// load an INFO JSON response string that has an open door contact
// save("info_sensors_open", NEOHUB_JSON_TEST_STRING_INFO_SENSORS_OPEN);
infoResponse = NeoHubInfoResponse.createDeviceData(load("info_sensors_open"));
assertNotNull(infoResponse);
// existing contact device type 5 (OPEN)
device = infoResponse.getDeviceRecord("Back Door");
assertNotNull(device);
assertEquals("Back Door", device.getDeviceName());
assertTrue(device instanceof InfoRecord);
assertEquals(5, ((InfoRecord) device).getDeviceType());
assertTrue(device.isWindowOpen());
assertTrue(device.isBatteryLow());
}
/*
* From NeoHub rev2.6 onwards the READ_DCB command is "deprecated" so we can
* also test the replacement GET_SYSTEM command (valid CORF response)
*/
@Test
public void testGetSystemJson() {
// load GET_SYSTEM JSON response string
NeoHubReadDcbResponse dcbResponse;
dcbResponse = NeoHubReadDcbResponse.createSystemData(load("system"));
assertNotNull(dcbResponse);
assertEquals(SIUnits.CELSIUS, dcbResponse.getTemperatureUnit());
}
/*
* From NeoHub rev2.6 onwards the INFO command is "deprecated" so we must test
* the replacement GET_LIVE_DATA command
*/
@Test
public void testGetLiveDataJson() {
// load GET_LIVE_DATA JSON response string
NeoHubLiveDeviceData liveDataResponse = NeoHubLiveDeviceData.createDeviceData(load("live_data"));
assertNotNull(liveDataResponse);
// test the time stamps
assertEquals(1588494785, liveDataResponse.getTimestampEngineers());
assertEquals(0, liveDataResponse.getTimestampSystem());
// missing device
AbstractRecord device = liveDataResponse.getDeviceRecord("Aardvark");
assertNull(device);
// test an existing thermostat device
device = liveDataResponse.getDeviceRecord("Dining Room");
assertNotNull(device);
assertEquals("Dining Room", device.getDeviceName());
assertEquals(new BigDecimal("22.0"), device.getTargetTemperature());
assertEquals(new BigDecimal("22.2"), device.getActualTemperature());
assertEquals(new BigDecimal("20.50"), device.getFloorTemperature());
assertFalse(device.isStandby());
assertFalse(device.isHeating());
assertFalse(device.isPreHeating());
assertFalse(device.isTimerOn());
assertFalse(device.offline());
assertFalse(device.stateManual());
assertTrue(device.stateAuto());
assertFalse(device.isWindowOpen());
assertFalse(device.isBatteryLow());
// test a plug device (MANUAL OFF)
device = liveDataResponse.getDeviceRecord("Living Room South");
assertNotNull(device);
assertEquals("Living Room South", device.getDeviceName());
assertFalse(device.isTimerOn());
assertTrue(device.stateManual());
// test a plug device (MANUAL ON)
device = liveDataResponse.getDeviceRecord("Living Room North");
assertNotNull(device);
assertEquals("Living Room North", device.getDeviceName());
assertTrue(device.isTimerOn());
assertTrue(device.stateManual());
// test a plug device (AUTO OFF)
device = liveDataResponse.getDeviceRecord("Green Wall Watering");
assertNotNull(device);
assertEquals("Green Wall Watering", device.getDeviceName());
assertFalse(device.isTimerOn());
assertFalse(device.stateManual());
// test a device that is offline
device = liveDataResponse.getDeviceRecord("Shower Room");
assertNotNull(device);
assertEquals("Shower Room", device.getDeviceName());
assertTrue(device.offline());
// test a device with a low battery
device = liveDataResponse.getDeviceRecord("Conservatory");
assertNotNull(device);
assertEquals("Conservatory", device.getDeviceName());
assertTrue(device.isBatteryLow());
// test a device with an open window alarm
device = liveDataResponse.getDeviceRecord("Door Contact");
assertNotNull(device);
assertEquals("Door Contact", device.getDeviceName());
assertTrue(device.isWindowOpen());
// test a wireless temperature sensor
device = liveDataResponse.getDeviceRecord("Room Sensor");
assertNotNull(device);
assertEquals("Room Sensor", device.getDeviceName());
assertEquals(new BigDecimal("21.5"), device.getActualTemperature());
// test a repeater node
device = liveDataResponse.getDeviceRecord("repeaternode54473");
assertNotNull(device);
assertEquals("repeaternode54473", device.getDeviceName());
assertTrue(MATCHER_HEATMISER_REPEATER.matcher(device.getDeviceName()).matches());
}
/*
* From NeoHub rev2.6 onwards the INFO command is "deprecated" and the DEVICE_ID
* element is not returned in the GET_LIVE_DATA call so we must test the
* replacement GET_ENGINEERS command
*/
@Test
public void testGetEngineersJson() {
// load GET_ENGINEERS JSON response string
NeoHubGetEngineersData engResponse = NeoHubGetEngineersData.createEngineersData(load("engineers"));
assertNotNull(engResponse);
// test device ID (type 12 thermostat device)
assertEquals(12, engResponse.getDeviceType("Dining Room"));
// test device ID (type 6 plug device)
assertEquals(6, engResponse.getDeviceType("Living Room South"));
}
/*
* 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);
String responseJson = "";
try {
responseJson = socket.sendMessage(requestJson);
} catch (Exception e) {
assertTrue(false);
}
return responseJson;
}
/*
* Test the communications
*/
@Test
public void testCommunications() {
String responseJson = testCommunicationInner(CMD_CODE_INFO);
assertFalse(responseJson.isEmpty());
responseJson = testCommunicationInner(CMD_CODE_READ_DCB);
assertFalse(responseJson.isEmpty());
NeoHubReadDcbResponse dcbResponse = NeoHubReadDcbResponse.createSystemData(responseJson);
assertNotNull(dcbResponse);
long timeStamp = dcbResponse.timeStamp;
assertEquals(Instant.now().getEpochSecond(), timeStamp, 1);
responseJson = testCommunicationInner(CMD_CODE_GET_LIVE_DATA);
assertFalse(responseJson.isEmpty());
NeoHubLiveDeviceData liveDataResponse = NeoHubLiveDeviceData.createDeviceData(responseJson);
assertNotNull(liveDataResponse);
assertTrue(timeStamp > liveDataResponse.getTimestampEngineers());
assertTrue(timeStamp > liveDataResponse.getTimestampSystem());
responseJson = testCommunicationInner(CMD_CODE_GET_ENGINEERS);
assertFalse(responseJson.isEmpty());
responseJson = testCommunicationInner(CMD_CODE_GET_SYSTEM);
assertFalse(responseJson.isEmpty());
responseJson = testCommunicationInner(String.format(CMD_CODE_TEMP, "20", "Hallway"));
assertFalse(responseJson.isEmpty());
}
}

View File

@@ -0,0 +1,45 @@
{
"AIR_ALT_DELAY": 60,
"AIR_ALT_DELAY_END": 6,
"AIR_ALT_DELAY_START": 23,
"ALT_TIMER_FORMAT": 1,
"AWAY": false,
"BOOTTIME": 1587842223,
"CLOSE_DELAY": null,
"COOLBOX": "heating",
"COOLBOX_PRESENT": 0,
"CORF": "C",
"DATE": "Apr 30",
"DEVICE_ID": "NeoHub",
"DSTAUTO": true,
"DSTON": true,
"EXTENDED_HISTORY": "off",
"Firmware version": 2134,
"GDEVLIST": [
1,
2,
3,
4,
5,
6,
7,
8,
9,
10
],
"GLOBAL_SYSTEM_TYPE": "HeatOnly",
"HEATING_LEVELS": 4,
"HEATORCOOL": "HEAT",
"Homekit": true,
"LAST_RECONNECT": 191959,
"LAST_RECONNECT_TIME": 1588034182,
"NTP": "Running",
"OPEN_DELAY": null,
"PARTITION COUNT": "4",
"PROGFORMAT": 1,
"SET_GLOBAL_HC_MODE": "heating",
"TIME": "16:24:35",
"TIMEZONE": 0.0,
"UPTIME": 418052,
"ZIGBEE_CHANNEL": "15"
}

View File

@@ -0,0 +1,45 @@
{
"AIR_ALT_DELAY": 60,
"AIR_ALT_DELAY_END": 6,
"AIR_ALT_DELAY_START": 23,
"ALT_TIMER_FORMAT": 1,
"AWAY": false,
"BOOTTIME": 1587842223,
"CLOSE_DELAY": null,
"COOLBOX": "heating",
"COOLBOX_PRESENT": 0,
"CORF": "",
"DATE": "Apr 30",
"DEVICE_ID": "NeoHub",
"DSTAUTO": true,
"DSTON": true,
"EXTENDED_HISTORY": "off",
"Firmware version": 2134,
"GDEVLIST": [
1,
2,
3,
4,
5,
6,
7,
8,
9,
10
],
"GLOBAL_SYSTEM_TYPE": "HeatOnly",
"HEATING_LEVELS": 4,
"HEATORCOOL": "HEAT",
"Homekit": true,
"LAST_RECONNECT": 191959,
"LAST_RECONNECT_TIME": 1588034182,
"NTP": "Running",
"OPEN_DELAY": null,
"PARTITION COUNT": "4",
"PROGFORMAT": 1,
"SET_GLOBAL_HC_MODE": "heating",
"TIME": "16:24:35",
"TIMEZONE": 0.0,
"UPTIME": 418052,
"ZIGBEE_CHANNEL": "15"
}

View File

@@ -0,0 +1,44 @@
{
"AIR_ALT_DELAY": 60,
"AIR_ALT_DELAY_END": 6,
"AIR_ALT_DELAY_START": 23,
"ALT_TIMER_FORMAT": 1,
"AWAY": false,
"BOOTTIME": 1587842223,
"CLOSE_DELAY": null,
"COOLBOX": "heating",
"COOLBOX_PRESENT": 0,
"DATE": "Apr 30",
"DEVICE_ID": "NeoHub",
"DSTAUTO": true,
"DSTON": true,
"EXTENDED_HISTORY": "off",
"Firmware version": 2134,
"GDEVLIST": [
1,
2,
3,
4,
5,
6,
7,
8,
9,
10
],
"GLOBAL_SYSTEM_TYPE": "HeatOnly",
"HEATING_LEVELS": 4,
"HEATORCOOL": "HEAT",
"Homekit": true,
"LAST_RECONNECT": 191959,
"LAST_RECONNECT_TIME": 1588034182,
"NTP": "Running",
"OPEN_DELAY": null,
"PARTITION COUNT": "4",
"PROGFORMAT": 1,
"SET_GLOBAL_HC_MODE": "heating",
"TIME": "16:24:35",
"TIMEZONE": 0.0,
"UPTIME": 418052,
"ZIGBEE_CHANNEL": "15"
}

View File

@@ -0,0 +1,45 @@
{
"AIR_ALT_DELAY": 60,
"AIR_ALT_DELAY_END": 6,
"AIR_ALT_DELAY_START": 23,
"ALT_TIMER_FORMAT": 1,
"AWAY": false,
"BOOTTIME": 1587842223,
"CLOSE_DELAY": null,
"COOLBOX": "heating",
"COOLBOX_PRESENT": 0,
"CORF": "F",
"DATE": "Apr 30",
"DEVICE_ID": "NeoHub",
"DSTAUTO": true,
"DSTON": true,
"EXTENDED_HISTORY": "off",
"Firmware version": 2134,
"GDEVLIST": [
1,
2,
3,
4,
5,
6,
7,
8,
9,
10
],
"GLOBAL_SYSTEM_TYPE": "HeatOnly",
"HEATING_LEVELS": 4,
"HEATORCOOL": "HEAT",
"Homekit": true,
"LAST_RECONNECT": 191959,
"LAST_RECONNECT_TIME": 1588034182,
"NTP": "Running",
"OPEN_DELAY": null,
"PARTITION COUNT": "4",
"PROGFORMAT": 1,
"SET_GLOBAL_HC_MODE": "heating",
"TIME": "16:24:35",
"TIMEZONE": 0.0,
"UPTIME": 418052,
"ZIGBEE_CHANNEL": "15"
}

View File

@@ -0,0 +1,211 @@
{
"Conservatory": {
"DEADBAND": 2,
"DEVICE_ID": 3,
"DEVICE_TYPE": 12,
"FLOOR_LIMIT": 38,
"FROST_TEMP": 12,
"MAX_PREHEAT": 2,
"OUTPUT_DELAY": 0,
"PUMP_DELAY": 0,
"RF_SENSOR_MODE": "self",
"STAT_FAILSAFE": 0,
"STAT_VERSION": 24,
"SWITCHING DIFFERENTIAL": 1,
"SWITCH_DELAY": 0,
"SYSTEM_TYPE": 0,
"TIMESTAMP": 1588493839,
"USER_LIMIT": 0,
"WINDOW_SWITCH_OPEN": false
},
"Dining Room": {
"DEADBAND": 2,
"DEVICE_ID": 1,
"DEVICE_TYPE": 12,
"FLOOR_LIMIT": 37,
"FROST_TEMP": 12,
"MAX_PREHEAT": 2,
"OUTPUT_DELAY": 0,
"PUMP_DELAY": 0,
"RF_SENSOR_MODE": "self",
"STAT_FAILSAFE": 0,
"STAT_VERSION": 24,
"SWITCHING DIFFERENTIAL": 1,
"SWITCH_DELAY": 0,
"SYSTEM_TYPE": 0,
"TIMESTAMP": 1588493858,
"USER_LIMIT": 0,
"WINDOW_SWITCH_OPEN": false
},
"Door Contact": {
"DEADBAND": 0,
"DEVICE_ID": 11,
"DEVICE_TYPE": 5,
"FLOOR_LIMIT": 0,
"FROST_TEMP": 0,
"MAX_PREHEAT": 0,
"OUTPUT_DELAY": 0,
"PUMP_DELAY": 0,
"RF_SENSOR_MODE": "self",
"STAT_FAILSAFE": 0,
"STAT_VERSION": 0,
"SWITCHING DIFFERENTIAL": 0,
"SWITCH_DELAY": 0,
"SYSTEM_TYPE": 0,
"TIMESTAMP": 1588700958,
"USER_LIMIT": 0,
"WINDOW_SWITCH_OPEN": true
},
"Green Wall Watering": {
"DEADBAND": 0,
"DEVICE_ID": 10,
"DEVICE_TYPE": 6,
"FLOOR_LIMIT": 0,
"FROST_TEMP": 0,
"MAX_PREHEAT": 0,
"OUTPUT_DELAY": 0,
"PUMP_DELAY": 0,
"RF_SENSOR_MODE": "self",
"STAT_FAILSAFE": 0,
"STAT_VERSION": 79,
"SWITCHING DIFFERENTIAL": 0,
"SWITCH_DELAY": 0,
"SYSTEM_TYPE": 0,
"TIMESTAMP": 1588494897,
"USER_LIMIT": 0,
"WINDOW_SWITCH_OPEN": false
},
"Hallway": {
"DEADBAND": 2,
"DEVICE_ID": 6,
"DEVICE_TYPE": 12,
"FLOOR_LIMIT": 37,
"FROST_TEMP": 12,
"MAX_PREHEAT": 2,
"OUTPUT_DELAY": 0,
"PUMP_DELAY": 0,
"RF_SENSOR_MODE": "self",
"STAT_FAILSAFE": 0,
"STAT_VERSION": 24,
"SWITCHING DIFFERENTIAL": 1,
"SWITCH_DELAY": 0,
"SYSTEM_TYPE": 0,
"TIMESTAMP": 1588493838,
"USER_LIMIT": 0,
"WINDOW_SWITCH_OPEN": false
},
"Kitchen": {
"DEADBAND": 2,
"DEVICE_ID": 5,
"DEVICE_TYPE": 12,
"FLOOR_LIMIT": 37,
"FROST_TEMP": 12,
"MAX_PREHEAT": 2,
"OUTPUT_DELAY": 0,
"PUMP_DELAY": 0,
"RF_SENSOR_MODE": "self",
"STAT_FAILSAFE": 0,
"STAT_VERSION": 24,
"SWITCHING DIFFERENTIAL": 1,
"SWITCH_DELAY": 0,
"SYSTEM_TYPE": 0,
"TIMESTAMP": 1588493861,
"USER_LIMIT": 0,
"WINDOW_SWITCH_OPEN": false
},
"Living Room": {
"DEADBAND": 2,
"DEVICE_ID": 4,
"DEVICE_TYPE": 12,
"FLOOR_LIMIT": 37,
"FROST_TEMP": 12,
"MAX_PREHEAT": 2,
"OUTPUT_DELAY": 0,
"PUMP_DELAY": 0,
"RF_SENSOR_MODE": "self",
"STAT_FAILSAFE": 0,
"STAT_VERSION": 24,
"SWITCHING DIFFERENTIAL": 1,
"SWITCH_DELAY": 0,
"SYSTEM_TYPE": 0,
"TIMESTAMP": 1588493838,
"USER_LIMIT": 0,
"WINDOW_SWITCH_OPEN": false
},
"Living Room North": {
"DEADBAND": 0,
"DEVICE_ID": 9,
"DEVICE_TYPE": 6,
"FLOOR_LIMIT": 0,
"FROST_TEMP": 0,
"MAX_PREHEAT": 0,
"OUTPUT_DELAY": 0,
"PUMP_DELAY": 0,
"RF_SENSOR_MODE": "self",
"STAT_FAILSAFE": 0,
"STAT_VERSION": 79,
"SWITCHING DIFFERENTIAL": 0,
"SWITCH_DELAY": 0,
"SYSTEM_TYPE": 0,
"TIMESTAMP": 1588493846,
"USER_LIMIT": 0,
"WINDOW_SWITCH_OPEN": false
},
"Living Room South": {
"DEADBAND": 0,
"DEVICE_ID": 8,
"DEVICE_TYPE": 6,
"FLOOR_LIMIT": 0,
"FROST_TEMP": 0,
"MAX_PREHEAT": 0,
"OUTPUT_DELAY": 0,
"PUMP_DELAY": 0,
"RF_SENSOR_MODE": "self",
"STAT_FAILSAFE": 0,
"STAT_VERSION": 79,
"SWITCHING DIFFERENTIAL": 0,
"SWITCH_DELAY": 0,
"SYSTEM_TYPE": 0,
"TIMESTAMP": 1588493846,
"USER_LIMIT": 0,
"WINDOW_SWITCH_OPEN": false
},
"Shed Heating": {
"DEADBAND": 2,
"DEVICE_ID": 7,
"DEVICE_TYPE": 12,
"FLOOR_LIMIT": 28,
"FROST_TEMP": 9,
"MAX_PREHEAT": 0,
"OUTPUT_DELAY": 0,
"PUMP_DELAY": 0,
"RF_SENSOR_MODE": "self",
"STAT_FAILSAFE": 0,
"STAT_VERSION": 24,
"SWITCHING DIFFERENTIAL": 1,
"SWITCH_DELAY": 0,
"SYSTEM_TYPE": 0,
"TIMESTAMP": 1588493854,
"USER_LIMIT": 0,
"WINDOW_SWITCH_OPEN": false
},
"Shower Room": {
"DEADBAND": 2,
"DEVICE_ID": 2,
"DEVICE_TYPE": 12,
"FLOOR_LIMIT": 28,
"FROST_TEMP": 12,
"MAX_PREHEAT": 2,
"OUTPUT_DELAY": 0,
"PUMP_DELAY": 0,
"RF_SENSOR_MODE": "self",
"STAT_FAILSAFE": 0,
"STAT_VERSION": 24,
"SWITCHING DIFFERENTIAL": 1,
"SWITCH_DELAY": 0,
"SYSTEM_TYPE": 0,
"TIMESTAMP": 1588493860,
"USER_LIMIT": 0,
"WINDOW_SWITCH_OPEN": false
}
}

View File

@@ -0,0 +1,730 @@
{
"devices": [
{
"AWAY": false,
"COOLING": false,
"COOLING_ENABLED": false,
"COOLING_TEMPERATURE_IN_WHOLE_DEGREES": 23,
"COOL_INP": false,
"COUNT_DOWN_TIME": "0:00",
"CRADLE_PAIRED_TO_REMOTE_SENSOR": false,
"CRADLE_PAIRED_TO_STAT": false,
"CURRENT_FLOOR_TEMPERATURE": 21,
"CURRENT_SET_TEMPERATURE": "22.0",
"CURRENT_TEMPERATURE": "24.0",
"DEMAND": false,
"DEVICE_TYPE": 12,
"ENABLE_BOILER": false,
"ENABLE_COOLING": false,
"ENABLE_PUMP": false,
"ENABLE_VALVE": false,
"ENABLE_ZONE": false,
"FAILSAFE_STATE": false,
"FAIL_SAFE_ENABLED": false,
"FLOOR_LIMIT": false,
"FULL/PARTIAL_LOCK_AVAILABLE": false,
"HEAT/COOL_MODE": false,
"HEATING": false,
"HOLD_TEMPERATURE": 21,
"HOLD_TIME": "0:00",
"HOLIDAY": false,
"HOLIDAY_DAYS": 0,
"HUMIDITY": 0,
"LOCK": false,
"LOCK_PIN_NUMBER": "0000",
"LOW_BATTERY": false,
"MAX_TEMPERATURE": "24.0",
"MIN_TEMPERATURE": "21.0",
"MODULATION_LEVEL": 0,
"NEXT_ON_TIME": "3 days 23:00",
"OFFLINE": 0,
"OUPUT_DELAY": false,
"OUTPUT_DELAY": 0,
"PREHEAT": false,
"PREHEAT_TIME": "3:24",
"PROGRAM_MODE": "24HOURSFIXED",
"PUMP_DELAY": false,
"RADIATORS_OR_UNDERFLOOR": false,
"SENSOR_SELECTION": "BUILT_IN_AND_FLOOR",
"SET_COUNTDOWN_TIME": 0,
"STANDBY": false,
"STAT_MODE": {
"4_HEAT_LEVELS": true,
"MANUAL_OFF": true,
"THERMOSTAT": true
},
"TEMPERATURE_FORMAT": false,
"TEMP_HOLD": false,
"TIMECLOCK_MODE": false,
"TIMER": false,
"TIME_CLOCK_OVERIDE_BIT": false,
"ULTRA_VERSION": 0,
"VERSION_NUMBER": 24,
"WRITE_COUNT": 0,
"ZONE_1PAIRED_TO_MULTILINK": true,
"ZONE_1_OR_2": false,
"ZONE_2_PAIRED_TO_MULTILINK": false,
"device": "Dining Room"
},
{
"AWAY": false,
"COOLING": false,
"COOLING_ENABLED": false,
"COOLING_TEMPERATURE_IN_WHOLE_DEGREES": 23,
"COOL_INP": false,
"COUNT_DOWN_TIME": "0:00",
"CRADLE_PAIRED_TO_REMOTE_SENSOR": false,
"CRADLE_PAIRED_TO_STAT": false,
"CURRENT_FLOOR_TEMPERATURE": 127,
"CURRENT_SET_TEMPERATURE": "22.0",
"CURRENT_TEMPERATURE": "22.9",
"DEMAND": false,
"DEVICE_TYPE": 12,
"ENABLE_BOILER": false,
"ENABLE_COOLING": false,
"ENABLE_PUMP": false,
"ENABLE_VALVE": false,
"ENABLE_ZONE": false,
"FAILSAFE_STATE": false,
"FAIL_SAFE_ENABLED": false,
"FLOOR_LIMIT": false,
"FULL/PARTIAL_LOCK_AVAILABLE": false,
"HEAT/COOL_MODE": false,
"HEATING": false,
"HOLD_TEMPERATURE": 23,
"HOLD_TIME": "0:00",
"HOLIDAY": false,
"HOLIDAY_DAYS": 0,
"HUMIDITY": 0,
"LOCK": false,
"LOCK_PIN_NUMBER": "0000",
"LOW_BATTERY": false,
"MAX_TEMPERATURE": "26.0",
"MIN_TEMPERATURE": "23.0",
"MODULATION_LEVEL": 0,
"NEXT_ON_TIME": "3 days 23:00",
"OFFLINE": 0,
"OUPUT_DELAY": false,
"OUTPUT_DELAY": 0,
"PREHEAT": false,
"PREHEAT_TIME": "255:255",
"PROGRAM_MODE": "24HOURSFIXED",
"PUMP_DELAY": false,
"RADIATORS_OR_UNDERFLOOR": false,
"SENSOR_SELECTION": "REMOTE_AIR_SENSOR",
"SET_COUNTDOWN_TIME": 0,
"STANDBY": false,
"STAT_MODE": {
"4_HEAT_LEVELS": true,
"MANUAL_OFF": true,
"THERMOSTAT": true
},
"TEMPERATURE_FORMAT": false,
"TEMP_HOLD": false,
"TIMECLOCK_MODE": false,
"TIMER": false,
"TIME_CLOCK_OVERIDE_BIT": false,
"ULTRA_VERSION": 0,
"VERSION_NUMBER": 24,
"WRITE_COUNT": 0,
"ZONE_1PAIRED_TO_MULTILINK": true,
"ZONE_1_OR_2": false,
"ZONE_2_PAIRED_TO_MULTILINK": false,
"device": "Shower Room"
},
{
"AWAY": false,
"COOLING": false,
"COOLING_ENABLED": false,
"COOLING_TEMPERATURE_IN_WHOLE_DEGREES": 23,
"COOL_INP": false,
"COUNT_DOWN_TIME": "0:00",
"CRADLE_PAIRED_TO_REMOTE_SENSOR": false,
"CRADLE_PAIRED_TO_STAT": false,
"CURRENT_FLOOR_TEMPERATURE": 23,
"CURRENT_SET_TEMPERATURE": "22.0",
"CURRENT_TEMPERATURE": "25.7",
"DEMAND": false,
"DEVICE_TYPE": 12,
"ENABLE_BOILER": false,
"ENABLE_COOLING": false,
"ENABLE_PUMP": false,
"ENABLE_VALVE": false,
"ENABLE_ZONE": false,
"FAILSAFE_STATE": false,
"FAIL_SAFE_ENABLED": false,
"FLOOR_LIMIT": false,
"FULL/PARTIAL_LOCK_AVAILABLE": false,
"HEAT/COOL_MODE": false,
"HEATING": false,
"HOLD_TEMPERATURE": 16,
"HOLD_TIME": "0:00",
"HOLIDAY": false,
"HOLIDAY_DAYS": 0,
"HUMIDITY": 0,
"LOCK": false,
"LOCK_PIN_NUMBER": "0000",
"LOW_BATTERY": false,
"MAX_TEMPERATURE": "26.0",
"MIN_TEMPERATURE": "21.0",
"MODULATION_LEVEL": 0,
"NEXT_ON_TIME": "3 days 23:00",
"OFFLINE": 0,
"OUPUT_DELAY": false,
"OUTPUT_DELAY": 0,
"PREHEAT": false,
"PREHEAT_TIME": "255:255",
"PROGRAM_MODE": "24HOURSFIXED",
"PUMP_DELAY": false,
"RADIATORS_OR_UNDERFLOOR": false,
"SENSOR_SELECTION": "BUILT_IN_AND_FLOOR",
"SET_COUNTDOWN_TIME": 0,
"STANDBY": false,
"STAT_MODE": {
"4_HEAT_LEVELS": true,
"MANUAL_OFF": true,
"THERMOSTAT": true
},
"TEMPERATURE_FORMAT": false,
"TEMP_HOLD": false,
"TIMECLOCK_MODE": false,
"TIMER": false,
"TIME_CLOCK_OVERIDE_BIT": false,
"ULTRA_VERSION": 0,
"VERSION_NUMBER": 24,
"WRITE_COUNT": 0,
"ZONE_1PAIRED_TO_MULTILINK": true,
"ZONE_1_OR_2": false,
"ZONE_2_PAIRED_TO_MULTILINK": false,
"device": "Conservatory"
},
{
"AWAY": false,
"COOLING": false,
"COOLING_ENABLED": false,
"COOLING_TEMPERATURE_IN_WHOLE_DEGREES": 23,
"COOL_INP": false,
"COUNT_DOWN_TIME": "0:00",
"CRADLE_PAIRED_TO_REMOTE_SENSOR": false,
"CRADLE_PAIRED_TO_STAT": false,
"CURRENT_FLOOR_TEMPERATURE": 27,
"CURRENT_SET_TEMPERATURE": "22.0",
"CURRENT_TEMPERATURE": "25.7",
"DEMAND": false,
"DEVICE_TYPE": 12,
"ENABLE_BOILER": false,
"ENABLE_COOLING": false,
"ENABLE_PUMP": false,
"ENABLE_VALVE": false,
"ENABLE_ZONE": false,
"FAILSAFE_STATE": false,
"FAIL_SAFE_ENABLED": false,
"FLOOR_LIMIT": false,
"FULL/PARTIAL_LOCK_AVAILABLE": false,
"HEAT/COOL_MODE": false,
"HEATING": false,
"HOLD_TEMPERATURE": 21,
"HOLD_TIME": "0:00",
"HOLIDAY": false,
"HOLIDAY_DAYS": 0,
"HUMIDITY": 0,
"LOCK": false,
"LOCK_PIN_NUMBER": "0000",
"LOW_BATTERY": false,
"MAX_TEMPERATURE": "26.0",
"MIN_TEMPERATURE": "21.0",
"MODULATION_LEVEL": 0,
"NEXT_ON_TIME": "3 days 23:00",
"OFFLINE": 0,
"OUPUT_DELAY": false,
"OUTPUT_DELAY": 0,
"PREHEAT": false,
"PREHEAT_TIME": "3:24",
"PROGRAM_MODE": "24HOURSFIXED",
"PUMP_DELAY": false,
"RADIATORS_OR_UNDERFLOOR": false,
"SENSOR_SELECTION": "BUILT_IN_AND_FLOOR",
"SET_COUNTDOWN_TIME": 0,
"STANDBY": false,
"STAT_MODE": {
"4_HEAT_LEVELS": true,
"MANUAL_OFF": true,
"THERMOSTAT": true
},
"TEMPERATURE_FORMAT": false,
"TEMP_HOLD": false,
"TIMECLOCK_MODE": false,
"TIMER": false,
"TIME_CLOCK_OVERIDE_BIT": false,
"ULTRA_VERSION": 0,
"VERSION_NUMBER": 24,
"WRITE_COUNT": 0,
"ZONE_1PAIRED_TO_MULTILINK": true,
"ZONE_1_OR_2": false,
"ZONE_2_PAIRED_TO_MULTILINK": false,
"device": "Living Room"
},
{
"AWAY": false,
"COOLING": false,
"COOLING_ENABLED": false,
"COOLING_TEMPERATURE_IN_WHOLE_DEGREES": 23,
"COOL_INP": false,
"COUNT_DOWN_TIME": "0:00",
"CRADLE_PAIRED_TO_REMOTE_SENSOR": false,
"CRADLE_PAIRED_TO_STAT": false,
"CURRENT_FLOOR_TEMPERATURE": 19,
"CURRENT_SET_TEMPERATURE": "21.0",
"CURRENT_TEMPERATURE": "23.0",
"DEMAND": false,
"DEVICE_TYPE": 12,
"ENABLE_BOILER": false,
"ENABLE_COOLING": false,
"ENABLE_PUMP": false,
"ENABLE_VALVE": false,
"ENABLE_ZONE": false,
"FAILSAFE_STATE": false,
"FAIL_SAFE_ENABLED": false,
"FLOOR_LIMIT": false,
"FULL/PARTIAL_LOCK_AVAILABLE": false,
"HEAT/COOL_MODE": false,
"HEATING": false,
"HOLD_TEMPERATURE": 21,
"HOLD_TIME": "0:00",
"HOLIDAY": false,
"HOLIDAY_DAYS": 0,
"HUMIDITY": 0,
"LOCK": false,
"LOCK_PIN_NUMBER": "0000",
"LOW_BATTERY": false,
"MAX_TEMPERATURE": "24.0",
"MIN_TEMPERATURE": "20.0",
"MODULATION_LEVEL": 0,
"NEXT_ON_TIME": "3 days 23:00",
"OFFLINE": 0,
"OUPUT_DELAY": false,
"OUTPUT_DELAY": 0,
"PREHEAT": false,
"PREHEAT_TIME": "255:255",
"PROGRAM_MODE": "24HOURSFIXED",
"PUMP_DELAY": false,
"RADIATORS_OR_UNDERFLOOR": false,
"SENSOR_SELECTION": "BUILT_IN_AND_FLOOR",
"SET_COUNTDOWN_TIME": 0,
"STANDBY": false,
"STAT_MODE": {
"4_HEAT_LEVELS": true,
"MANUAL_OFF": true,
"THERMOSTAT": true
},
"TEMPERATURE_FORMAT": false,
"TEMP_HOLD": false,
"TIMECLOCK_MODE": false,
"TIMER": false,
"TIME_CLOCK_OVERIDE_BIT": false,
"ULTRA_VERSION": 0,
"VERSION_NUMBER": 24,
"WRITE_COUNT": 0,
"ZONE_1PAIRED_TO_MULTILINK": true,
"ZONE_1_OR_2": false,
"ZONE_2_PAIRED_TO_MULTILINK": false,
"device": "Kitchen"
},
{
"AWAY": false,
"COOLING": false,
"COOLING_ENABLED": false,
"COOLING_TEMPERATURE_IN_WHOLE_DEGREES": 23,
"COOL_INP": false,
"COUNT_DOWN_TIME": "0:00",
"CRADLE_PAIRED_TO_REMOTE_SENSOR": false,
"CRADLE_PAIRED_TO_STAT": false,
"CURRENT_FLOOR_TEMPERATURE": 19,
"CURRENT_SET_TEMPERATURE": "20.0",
"CURRENT_TEMPERATURE": "21.7",
"DEMAND": false,
"DEVICE_TYPE": 12,
"ENABLE_BOILER": false,
"ENABLE_COOLING": false,
"ENABLE_PUMP": false,
"ENABLE_VALVE": false,
"ENABLE_ZONE": false,
"FAILSAFE_STATE": false,
"FAIL_SAFE_ENABLED": false,
"FLOOR_LIMIT": false,
"FULL/PARTIAL_LOCK_AVAILABLE": false,
"HEAT/COOL_MODE": false,
"HEATING": false,
"HOLD_TEMPERATURE": 21,
"HOLD_TIME": "0:00",
"HOLIDAY": false,
"HOLIDAY_DAYS": 0,
"HUMIDITY": 0,
"LOCK": false,
"LOCK_PIN_NUMBER": "0000",
"LOW_BATTERY": false,
"MAX_TEMPERATURE": "23.0",
"MIN_TEMPERATURE": "21.0",
"MODULATION_LEVEL": 0,
"NEXT_ON_TIME": "3 days 22:00",
"OFFLINE": 0,
"OUPUT_DELAY": false,
"OUTPUT_DELAY": 0,
"PREHEAT": false,
"PREHEAT_TIME": "255:255",
"PROGRAM_MODE": "24HOURSFIXED",
"PUMP_DELAY": false,
"RADIATORS_OR_UNDERFLOOR": false,
"SENSOR_SELECTION": "BUILT_IN_AND_FLOOR",
"SET_COUNTDOWN_TIME": 0,
"STANDBY": false,
"STAT_MODE": {
"4_HEAT_LEVELS": true,
"MANUAL_OFF": true,
"THERMOSTAT": true
},
"TEMPERATURE_FORMAT": false,
"TEMP_HOLD": false,
"TIMECLOCK_MODE": false,
"TIMER": false,
"TIME_CLOCK_OVERIDE_BIT": false,
"ULTRA_VERSION": 0,
"VERSION_NUMBER": 24,
"WRITE_COUNT": 0,
"ZONE_1PAIRED_TO_MULTILINK": true,
"ZONE_1_OR_2": false,
"ZONE_2_PAIRED_TO_MULTILINK": false,
"device": "Hallway"
},
{
"AWAY": false,
"COOLING": false,
"COOLING_ENABLED": false,
"COOLING_TEMPERATURE_IN_WHOLE_DEGREES": 23,
"COOL_INP": false,
"COUNT_DOWN_TIME": "0:00",
"CRADLE_PAIRED_TO_REMOTE_SENSOR": false,
"CRADLE_PAIRED_TO_STAT": false,
"CURRENT_FLOOR_TEMPERATURE": 127,
"CURRENT_SET_TEMPERATURE": "9.0",
"CURRENT_TEMPERATURE": "19.4",
"DEMAND": false,
"DEVICE_TYPE": 12,
"ENABLE_BOILER": false,
"ENABLE_COOLING": false,
"ENABLE_PUMP": false,
"ENABLE_VALVE": false,
"ENABLE_ZONE": false,
"FAILSAFE_STATE": false,
"FAIL_SAFE_ENABLED": false,
"FLOOR_LIMIT": false,
"FULL/PARTIAL_LOCK_AVAILABLE": false,
"HEAT/COOL_MODE": false,
"HEATING": false,
"HOLD_TEMPERATURE": 9,
"HOLD_TIME": "0:00",
"HOLIDAY": false,
"HOLIDAY_DAYS": 0,
"HUMIDITY": 0,
"LOCK": false,
"LOCK_PIN_NUMBER": "0000",
"LOW_BATTERY": false,
"MAX_TEMPERATURE": "22.0",
"MIN_TEMPERATURE": "15.0",
"MODULATION_LEVEL": 0,
"NEXT_ON_TIME": "4 days 08:00",
"OFFLINE": 0,
"OUPUT_DELAY": false,
"OUTPUT_DELAY": 5,
"PREHEAT": false,
"PREHEAT_TIME": "255:255",
"PROGRAM_MODE": "24HOURSFIXED",
"PUMP_DELAY": false,
"RADIATORS_OR_UNDERFLOOR": false,
"SENSOR_SELECTION": "BUILT_IN_AIR_SENSOR",
"SET_COUNTDOWN_TIME": 0,
"STANDBY": false,
"STAT_MODE": {
"4_HEAT_LEVELS": true,
"MANUAL_OFF": true,
"THERMOSTAT": true
},
"TEMPERATURE_FORMAT": false,
"TEMP_HOLD": false,
"TIMECLOCK_MODE": false,
"TIMER": false,
"TIME_CLOCK_OVERIDE_BIT": false,
"ULTRA_VERSION": 0,
"VERSION_NUMBER": 24,
"WRITE_COUNT": 1,
"ZONE_1PAIRED_TO_MULTILINK": true,
"ZONE_1_OR_2": false,
"ZONE_2_PAIRED_TO_MULTILINK": false,
"device": "Shed Heating"
},
{
"AWAY": false,
"COOLING": false,
"COOLING_ENABLED": false,
"COOLING_TEMPERATURE_IN_WHOLE_DEGREES": 0,
"COOL_INP": false,
"COUNT_DOWN_TIME": "0:00",
"CRADLE_PAIRED_TO_REMOTE_SENSOR": false,
"CRADLE_PAIRED_TO_STAT": false,
"CURRENT_FLOOR_TEMPERATURE": 127,
"CURRENT_SET_TEMPERATURE": "0.0",
"CURRENT_TEMPERATURE": "255.255",
"DEMAND": false,
"DEVICE_TYPE": 6,
"ENABLE_BOILER": false,
"ENABLE_COOLING": false,
"ENABLE_PUMP": false,
"ENABLE_VALVE": false,
"ENABLE_ZONE": false,
"FAILSAFE_STATE": false,
"FAIL_SAFE_ENABLED": false,
"FLOOR_LIMIT": false,
"FULL/PARTIAL_LOCK_AVAILABLE": false,
"HEAT/COOL_MODE": false,
"HEATING": false,
"HOLD_TEMPERATURE": 1,
"HOLD_TIME": "0:00",
"HOLIDAY": false,
"HOLIDAY_DAYS": 0,
"HUMIDITY": 0,
"LOCK": false,
"LOCK_PIN_NUMBER": "0000",
"LOW_BATTERY": false,
"MAX_TEMPERATURE": "255.255",
"MIN_TEMPERATURE": "255.255",
"MODULATION_LEVEL": 0,
"NEXT_ON_TIME": "255 days 255:255",
"OFFLINE": 0,
"OUPUT_DELAY": false,
"OUTPUT_DELAY": 0,
"PREHEAT": false,
"PREHEAT_TIME": "255:255",
"PROGRAM_MODE": "24HOURSFIXED",
"PUMP_DELAY": false,
"RADIATORS_OR_UNDERFLOOR": false,
"SENSOR_SELECTION": "BUILT_IN_AIR_SENSOR",
"SET_COUNTDOWN_TIME": 0,
"STANDBY": false,
"STAT_MODE": {
"4_HEAT_LEVELS": true,
"MANUAL_ON": true,
"TIMECLOCK": true
},
"TEMPERATURE_FORMAT": false,
"TEMP_HOLD": false,
"TIMECLOCK_MODE": false,
"TIMER": false,
"TIME_CLOCK_OVERIDE_BIT": false,
"ULTRA_VERSION": 0,
"VERSION_NUMBER": 79,
"WRITE_COUNT": 11,
"ZONE_1PAIRED_TO_MULTILINK": true,
"ZONE_1_OR_2": false,
"ZONE_2_PAIRED_TO_MULTILINK": false,
"device": "Living Room South"
},
{
"AWAY": false,
"COOLING": false,
"COOLING_ENABLED": false,
"COOLING_TEMPERATURE_IN_WHOLE_DEGREES": 0,
"COOL_INP": false,
"COUNT_DOWN_TIME": "0:00",
"CRADLE_PAIRED_TO_REMOTE_SENSOR": false,
"CRADLE_PAIRED_TO_STAT": false,
"CURRENT_FLOOR_TEMPERATURE": 127,
"CURRENT_SET_TEMPERATURE": "0.0",
"CURRENT_TEMPERATURE": "255.255",
"DEMAND": false,
"DEVICE_TYPE": 6,
"ENABLE_BOILER": false,
"ENABLE_COOLING": false,
"ENABLE_PUMP": false,
"ENABLE_VALVE": false,
"ENABLE_ZONE": false,
"FAILSAFE_STATE": false,
"FAIL_SAFE_ENABLED": false,
"FLOOR_LIMIT": false,
"FULL/PARTIAL_LOCK_AVAILABLE": false,
"HEAT/COOL_MODE": false,
"HEATING": false,
"HOLD_TEMPERATURE": 0,
"HOLD_TIME": "0:00",
"HOLIDAY": false,
"HOLIDAY_DAYS": 0,
"HUMIDITY": 0,
"LOCK": false,
"LOCK_PIN_NUMBER": "0000",
"LOW_BATTERY": false,
"MAX_TEMPERATURE": "255.255",
"MIN_TEMPERATURE": "255.255",
"MODULATION_LEVEL": 0,
"NEXT_ON_TIME": "255 days 255:255",
"OFFLINE": 0,
"OUPUT_DELAY": false,
"OUTPUT_DELAY": 0,
"PREHEAT": false,
"PREHEAT_TIME": "255:255",
"PROGRAM_MODE": "24HOURSFIXED",
"PUMP_DELAY": false,
"RADIATORS_OR_UNDERFLOOR": false,
"SENSOR_SELECTION": "BUILT_IN_AIR_SENSOR",
"SET_COUNTDOWN_TIME": 0,
"STANDBY": false,
"STAT_MODE": {
"4_HEAT_LEVELS": true,
"MANUAL_ON": true,
"TIMECLOCK": true
},
"TEMPERATURE_FORMAT": false,
"TEMP_HOLD": false,
"TIMECLOCK_MODE": false,
"TIMER": true,
"TIME_CLOCK_OVERIDE_BIT": true,
"ULTRA_VERSION": 0,
"VERSION_NUMBER": 79,
"WRITE_COUNT": 192,
"ZONE_1PAIRED_TO_MULTILINK": true,
"ZONE_1_OR_2": false,
"ZONE_2_PAIRED_TO_MULTILINK": false,
"device": "Living Room North"
},
{
"AWAY": false,
"COOLING": false,
"COOLING_ENABLED": false,
"COOLING_TEMPERATURE_IN_WHOLE_DEGREES": 0,
"COOL_INP": false,
"COUNT_DOWN_TIME": "0:00",
"CRADLE_PAIRED_TO_REMOTE_SENSOR": false,
"CRADLE_PAIRED_TO_STAT": false,
"CURRENT_FLOOR_TEMPERATURE": 127,
"CURRENT_SET_TEMPERATURE": "0.0",
"CURRENT_TEMPERATURE": "255.255",
"DEMAND": false,
"DEVICE_TYPE": 6,
"ENABLE_BOILER": false,
"ENABLE_COOLING": false,
"ENABLE_PUMP": false,
"ENABLE_VALVE": false,
"ENABLE_ZONE": false,
"FAILSAFE_STATE": false,
"FAIL_SAFE_ENABLED": false,
"FLOOR_LIMIT": false,
"FULL/PARTIAL_LOCK_AVAILABLE": false,
"HEAT/COOL_MODE": false,
"HEATING": false,
"HOLD_TEMPERATURE": 1,
"HOLD_TIME": "0:00",
"HOLIDAY": false,
"HOLIDAY_DAYS": 0,
"HUMIDITY": 0,
"LOCK": false,
"LOCK_PIN_NUMBER": "0000",
"LOW_BATTERY": false,
"MAX_TEMPERATURE": "255.255",
"MIN_TEMPERATURE": "255.255",
"MODULATION_LEVEL": 0,
"NEXT_ON_TIME": "255 days 255:255",
"OFFLINE": 0,
"OUPUT_DELAY": false,
"OUTPUT_DELAY": 0,
"PREHEAT": false,
"PREHEAT_TIME": "255:255",
"PROGRAM_MODE": "24HOURSFIXED",
"PUMP_DELAY": false,
"RADIATORS_OR_UNDERFLOOR": false,
"SENSOR_SELECTION": "BUILT_IN_AIR_SENSOR",
"SET_COUNTDOWN_TIME": 0,
"STANDBY": false,
"STAT_MODE": {
"4_HEAT_LEVELS": true,
"MANUAL_OFF": true,
"TIMECLOCK": true
},
"TEMPERATURE_FORMAT": false,
"TEMP_HOLD": false,
"TIMECLOCK_MODE": false,
"TIMER": false,
"TIME_CLOCK_OVERIDE_BIT": false,
"ULTRA_VERSION": 0,
"VERSION_NUMBER": 79,
"WRITE_COUNT": 57,
"ZONE_1PAIRED_TO_MULTILINK": true,
"ZONE_1_OR_2": false,
"ZONE_2_PAIRED_TO_MULTILINK": false,
"device": "Green Wall Watering"
},
{
"AWAY": false,
"COOLING": false,
"COOLING_ENABLED": false,
"COOLING_TEMPERATURE_IN_WHOLE_DEGREES": 0,
"COOL_INP": false,
"COUNT_DOWN_TIME": "0:00",
"CRADLE_PAIRED_TO_REMOTE_SENSOR": false,
"CRADLE_PAIRED_TO_STAT": false,
"CURRENT_FLOOR_TEMPERATURE": 127,
"CURRENT_SET_TEMPERATURE": "0.0",
"CURRENT_TEMPERATURE": "255.255",
"DEMAND": false,
"DEVICE_TYPE": 10,
"ENABLE_BOILER": false,
"ENABLE_COOLING": false,
"ENABLE_PUMP": false,
"ENABLE_VALVE": false,
"ENABLE_ZONE": false,
"FAILSAFE_STATE": false,
"FAIL_SAFE_ENABLED": false,
"FLOOR_LIMIT": false,
"FULL/PARTIAL_LOCK_AVAILABLE": false,
"HEAT/COOL_MODE": false,
"HEATING": false,
"HOLD_TEMPERATURE": 0,
"HOLD_TIME": "0:00",
"HOLIDAY": false,
"HOLIDAY_DAYS": 0,
"HUMIDITY": 0,
"LOCK": false,
"LOCK_PIN_NUMBER": "0000",
"LOW_BATTERY": false,
"MAX_TEMPERATURE": "255.255",
"MIN_TEMPERATURE": "255.255",
"MODULATION_LEVEL": 0,
"NEXT_ON_TIME": "255 days 255:255",
"OFFLINE": 0,
"OUPUT_DELAY": false,
"OUTPUT_DELAY": 0,
"PREHEAT": false,
"PREHEAT_TIME": "255:255",
"PROGRAM_MODE": "5DAY/2DAY",
"PUMP_DELAY": false,
"RADIATORS_OR_UNDERFLOOR": false,
"SENSOR_SELECTION": "BUILT_IN_AIR_SENSOR",
"SET_COUNTDOWN_TIME": 0,
"STANDBY": false,
"STAT_MODE": {
"4_HEAT_LEVELS": true,
"MANUAL_OFF": true,
"TIMECLOCK": true
},
"TEMPERATURE_FORMAT": false,
"TEMP_HOLD": false,
"TIMECLOCK_MODE": false,
"TIMER": false,
"TIME_CLOCK_OVERIDE_BIT": false,
"ULTRA_VERSION": 0,
"VERSION_NUMBER": 69,
"WRITE_COUNT": 0,
"ZONE_1PAIRED_TO_MULTILINK": true,
"ZONE_1_OR_2": false,
"ZONE_2_PAIRED_TO_MULTILINK": false,
"device": "repeaternode54473"
}
]
}

View File

@@ -0,0 +1,664 @@
{
"devices": [
{
"AWAY": false,
"COOLING": false,
"COOLING_ENABLED": false,
"COOLING_TEMPERATURE_IN_WHOLE_DEGREES": 23,
"COOL_INP": false,
"COUNT_DOWN_TIME": "0:00",
"CRADLE_PAIRED_TO_REMOTE_SENSOR": false,
"CRADLE_PAIRED_TO_STAT": false,
"CURRENT_FLOOR_TEMPERATURE": 23,
"CURRENT_SET_TEMPERATURE": "22.0",
"CURRENT_TEMPERATURE": "22.2",
"DEMAND": false,
"DEVICE_TYPE": 12,
"ENABLE_BOILER": false,
"ENABLE_COOLING": false,
"ENABLE_PUMP": false,
"ENABLE_VALVE": false,
"ENABLE_ZONE": false,
"FAILSAFE_STATE": false,
"FAIL_SAFE_ENABLED": false,
"FLOOR_LIMIT": false,
"FULL/PARTIAL_LOCK_AVAILABLE": false,
"HEAT/COOL_MODE": false,
"HEATING": false,
"HOLD_TEMPERATURE": 21,
"HOLD_TIME": "0:00",
"HOLIDAY": false,
"HOLIDAY_DAYS": 0,
"HUMIDITY": 0,
"LOCK": false,
"LOCK_PIN_NUMBER": "0000",
"LOW_BATTERY": false,
"MAX_TEMPERATURE": "22.0",
"MIN_TEMPERATURE": "21.0",
"MODULATION_LEVEL": 0,
"NEXT_ON_TIME": "6 days 23:00",
"OFFLINE": false,
"OUPUT_DELAY": false,
"OUTPUT_DELAY": 0,
"PREHEAT": false,
"PREHEAT_TIME": "2:42",
"PROGRAM_MODE": "24HOURSFIXED",
"PUMP_DELAY": false,
"RADIATORS_OR_UNDERFLOOR": false,
"SENSOR_SELECTION": "BUILT_IN_AND_FLOOR",
"SET_COUNTDOWN_TIME": 0,
"STANDBY": false,
"STAT_MODE": {
"4_HEAT_LEVELS": true,
"MANUAL_OFF": true,
"THERMOSTAT": true
},
"TEMPERATURE_FORMAT": false,
"TEMP_HOLD": false,
"TIMECLOCK_MODE": false,
"TIMER": false,
"TIME_CLOCK_OVERIDE_BIT": false,
"ULTRA_VERSION": 0,
"VERSION_NUMBER": 20,
"WRITE_COUNT": 0,
"ZONE_1PAIRED_TO_MULTILINK": true,
"ZONE_1_OR_2": false,
"ZONE_2_PAIRED_TO_MULTILINK": false,
"device": "Dining Room"
},
{
"AWAY": false,
"COOLING": false,
"COOLING_ENABLED": false,
"COOLING_TEMPERATURE_IN_WHOLE_DEGREES": 23,
"COOL_INP": false,
"COUNT_DOWN_TIME": "0:00",
"CRADLE_PAIRED_TO_REMOTE_SENSOR": false,
"CRADLE_PAIRED_TO_STAT": false,
"CURRENT_FLOOR_TEMPERATURE": 127,
"CURRENT_SET_TEMPERATURE": "22.0",
"CURRENT_TEMPERATURE": "22.6",
"DEMAND": false,
"DEVICE_TYPE": 12,
"ENABLE_BOILER": false,
"ENABLE_COOLING": false,
"ENABLE_PUMP": false,
"ENABLE_VALVE": false,
"ENABLE_ZONE": false,
"FAILSAFE_STATE": false,
"FAIL_SAFE_ENABLED": false,
"FLOOR_LIMIT": false,
"FULL/PARTIAL_LOCK_AVAILABLE": false,
"HEAT/COOL_MODE": false,
"HEATING": false,
"HOLD_TEMPERATURE": 23,
"HOLD_TIME": "0:00",
"HOLIDAY": false,
"HOLIDAY_DAYS": 0,
"HUMIDITY": 0,
"LOCK": false,
"LOCK_PIN_NUMBER": "0000",
"LOW_BATTERY": false,
"MAX_TEMPERATURE": "24.0",
"MIN_TEMPERATURE": "23.0",
"MODULATION_LEVEL": 0,
"NEXT_ON_TIME": "6 days 23:00",
"OFFLINE": false,
"OUPUT_DELAY": false,
"OUTPUT_DELAY": 0,
"PREHEAT": false,
"PREHEAT_TIME": "255:255",
"PROGRAM_MODE": "24HOURSFIXED",
"PUMP_DELAY": false,
"RADIATORS_OR_UNDERFLOOR": false,
"SENSOR_SELECTION": "REMOTE_AIR_SENSOR",
"SET_COUNTDOWN_TIME": 0,
"STANDBY": false,
"STAT_MODE": {
"4_HEAT_LEVELS": true,
"MANUAL_OFF": true,
"THERMOSTAT": true
},
"TEMPERATURE_FORMAT": false,
"TEMP_HOLD": false,
"TIMECLOCK_MODE": false,
"TIMER": false,
"TIME_CLOCK_OVERIDE_BIT": false,
"ULTRA_VERSION": 0,
"VERSION_NUMBER": 20,
"WRITE_COUNT": 4,
"ZONE_1PAIRED_TO_MULTILINK": true,
"ZONE_1_OR_2": false,
"ZONE_2_PAIRED_TO_MULTILINK": false,
"device": "Shower Room"
},
{
"AWAY": false,
"COOLING": false,
"COOLING_ENABLED": false,
"COOLING_TEMPERATURE_IN_WHOLE_DEGREES": 23,
"COOL_INP": false,
"COUNT_DOWN_TIME": "0:00",
"CRADLE_PAIRED_TO_REMOTE_SENSOR": false,
"CRADLE_PAIRED_TO_STAT": false,
"CURRENT_FLOOR_TEMPERATURE": 26,
"CURRENT_SET_TEMPERATURE": "22.0",
"CURRENT_TEMPERATURE": "23.3",
"DEMAND": false,
"DEVICE_TYPE": 12,
"ENABLE_BOILER": false,
"ENABLE_COOLING": false,
"ENABLE_PUMP": false,
"ENABLE_VALVE": false,
"ENABLE_ZONE": false,
"FAILSAFE_STATE": false,
"FAIL_SAFE_ENABLED": false,
"FLOOR_LIMIT": false,
"FULL/PARTIAL_LOCK_AVAILABLE": false,
"HEAT/COOL_MODE": false,
"HEATING": false,
"HOLD_TEMPERATURE": 16,
"HOLD_TIME": "0:00",
"HOLIDAY": false,
"HOLIDAY_DAYS": 0,
"HUMIDITY": 0,
"LOCK": false,
"LOCK_PIN_NUMBER": "0000",
"LOW_BATTERY": false,
"MAX_TEMPERATURE": "25.0",
"MIN_TEMPERATURE": "20.0",
"MODULATION_LEVEL": 0,
"NEXT_ON_TIME": "6 days 23:00",
"OFFLINE": false,
"OUPUT_DELAY": false,
"OUTPUT_DELAY": 0,
"PREHEAT": false,
"PREHEAT_TIME": "3:48",
"PROGRAM_MODE": "24HOURSFIXED",
"PUMP_DELAY": false,
"RADIATORS_OR_UNDERFLOOR": false,
"SENSOR_SELECTION": "BUILT_IN_AND_FLOOR",
"SET_COUNTDOWN_TIME": 0,
"STANDBY": false,
"STAT_MODE": {
"4_HEAT_LEVELS": true,
"MANUAL_OFF": true,
"THERMOSTAT": true
},
"TEMPERATURE_FORMAT": false,
"TEMP_HOLD": false,
"TIMECLOCK_MODE": false,
"TIMER": false,
"TIME_CLOCK_OVERIDE_BIT": false,
"ULTRA_VERSION": 0,
"VERSION_NUMBER": 20,
"WRITE_COUNT": 0,
"ZONE_1PAIRED_TO_MULTILINK": true,
"ZONE_1_OR_2": false,
"ZONE_2_PAIRED_TO_MULTILINK": false,
"device": "Conservatory"
},
{
"AWAY": false,
"COOLING": false,
"COOLING_ENABLED": false,
"COOLING_TEMPERATURE_IN_WHOLE_DEGREES": 23,
"COOL_INP": false,
"COUNT_DOWN_TIME": "0:00",
"CRADLE_PAIRED_TO_REMOTE_SENSOR": false,
"CRADLE_PAIRED_TO_STAT": false,
"CURRENT_FLOOR_TEMPERATURE": 28,
"CURRENT_SET_TEMPERATURE": "22.0",
"CURRENT_TEMPERATURE": "22.6",
"DEMAND": false,
"DEVICE_TYPE": 12,
"ENABLE_BOILER": false,
"ENABLE_COOLING": false,
"ENABLE_PUMP": false,
"ENABLE_VALVE": false,
"ENABLE_ZONE": false,
"FAILSAFE_STATE": false,
"FAIL_SAFE_ENABLED": false,
"FLOOR_LIMIT": false,
"FULL/PARTIAL_LOCK_AVAILABLE": false,
"HEAT/COOL_MODE": false,
"HEATING": false,
"HOLD_TEMPERATURE": 21,
"HOLD_TIME": "0:00",
"HOLIDAY": false,
"HOLIDAY_DAYS": 0,
"HUMIDITY": 0,
"LOCK": false,
"LOCK_PIN_NUMBER": "0000",
"LOW_BATTERY": false,
"MAX_TEMPERATURE": "24.0",
"MIN_TEMPERATURE": "19.0",
"MODULATION_LEVEL": 0,
"NEXT_ON_TIME": "6 days 23:00",
"OFFLINE": false,
"OUPUT_DELAY": false,
"OUTPUT_DELAY": 0,
"PREHEAT": false,
"PREHEAT_TIME": "3:16",
"PROGRAM_MODE": "24HOURSFIXED",
"PUMP_DELAY": false,
"RADIATORS_OR_UNDERFLOOR": false,
"SENSOR_SELECTION": "BUILT_IN_AND_FLOOR",
"SET_COUNTDOWN_TIME": 0,
"STANDBY": false,
"STAT_MODE": {
"4_HEAT_LEVELS": true,
"MANUAL_OFF": true,
"THERMOSTAT": true
},
"TEMPERATURE_FORMAT": false,
"TEMP_HOLD": false,
"TIMECLOCK_MODE": false,
"TIMER": false,
"TIME_CLOCK_OVERIDE_BIT": false,
"ULTRA_VERSION": 0,
"VERSION_NUMBER": 20,
"WRITE_COUNT": 2,
"ZONE_1PAIRED_TO_MULTILINK": true,
"ZONE_1_OR_2": false,
"ZONE_2_PAIRED_TO_MULTILINK": false,
"device": "Living Room"
},
{
"AWAY": false,
"COOLING": false,
"COOLING_ENABLED": false,
"COOLING_TEMPERATURE_IN_WHOLE_DEGREES": 23,
"COOL_INP": false,
"COUNT_DOWN_TIME": "0:00",
"CRADLE_PAIRED_TO_REMOTE_SENSOR": false,
"CRADLE_PAIRED_TO_STAT": false,
"CURRENT_FLOOR_TEMPERATURE": 23,
"CURRENT_SET_TEMPERATURE": "22.0",
"CURRENT_TEMPERATURE": "23.6",
"DEMAND": false,
"DEVICE_TYPE": 12,
"ENABLE_BOILER": false,
"ENABLE_COOLING": false,
"ENABLE_PUMP": false,
"ENABLE_VALVE": false,
"ENABLE_ZONE": false,
"FAILSAFE_STATE": false,
"FAIL_SAFE_ENABLED": false,
"FLOOR_LIMIT": false,
"FULL/PARTIAL_LOCK_AVAILABLE": false,
"HEAT/COOL_MODE": false,
"HEATING": false,
"HOLD_TEMPERATURE": 21,
"HOLD_TIME": "0:00",
"HOLIDAY": false,
"HOLIDAY_DAYS": 0,
"HUMIDITY": 0,
"LOCK": false,
"LOCK_PIN_NUMBER": "0000",
"LOW_BATTERY": false,
"MAX_TEMPERATURE": "23.0",
"MIN_TEMPERATURE": "20.0",
"MODULATION_LEVEL": 0,
"NEXT_ON_TIME": "6 days 23:00",
"OFFLINE": false,
"OUPUT_DELAY": false,
"OUTPUT_DELAY": 0,
"PREHEAT": false,
"PREHEAT_TIME": "2:08",
"PROGRAM_MODE": "24HOURSFIXED",
"PUMP_DELAY": false,
"RADIATORS_OR_UNDERFLOOR": false,
"SENSOR_SELECTION": "BUILT_IN_AND_FLOOR",
"SET_COUNTDOWN_TIME": 0,
"STANDBY": false,
"STAT_MODE": {
"4_HEAT_LEVELS": true,
"MANUAL_OFF": true,
"THERMOSTAT": true
},
"TEMPERATURE_FORMAT": false,
"TEMP_HOLD": false,
"TIMECLOCK_MODE": false,
"TIMER": false,
"TIME_CLOCK_OVERIDE_BIT": false,
"ULTRA_VERSION": 0,
"VERSION_NUMBER": 20,
"WRITE_COUNT": 6,
"ZONE_1PAIRED_TO_MULTILINK": true,
"ZONE_1_OR_2": false,
"ZONE_2_PAIRED_TO_MULTILINK": false,
"device": "Kitchen"
},
{
"AWAY": false,
"COOLING": false,
"COOLING_ENABLED": false,
"COOLING_TEMPERATURE_IN_WHOLE_DEGREES": 23,
"COOL_INP": false,
"COUNT_DOWN_TIME": "0:00",
"CRADLE_PAIRED_TO_REMOTE_SENSOR": false,
"CRADLE_PAIRED_TO_STAT": false,
"CURRENT_FLOOR_TEMPERATURE": 17,
"CURRENT_SET_TEMPERATURE": "20.0",
"CURRENT_TEMPERATURE": "20.1",
"DEMAND": false,
"DEVICE_TYPE": 12,
"ENABLE_BOILER": false,
"ENABLE_COOLING": false,
"ENABLE_PUMP": false,
"ENABLE_VALVE": false,
"ENABLE_ZONE": false,
"FAILSAFE_STATE": false,
"FAIL_SAFE_ENABLED": false,
"FLOOR_LIMIT": false,
"FULL/PARTIAL_LOCK_AVAILABLE": false,
"HEAT/COOL_MODE": false,
"HEATING": false,
"HOLD_TEMPERATURE": 21,
"HOLD_TIME": "0:00",
"HOLIDAY": false,
"HOLIDAY_DAYS": 0,
"HUMIDITY": 0,
"LOCK": false,
"LOCK_PIN_NUMBER": "0000",
"LOW_BATTERY": false,
"MAX_TEMPERATURE": "21.0",
"MIN_TEMPERATURE": "20.0",
"MODULATION_LEVEL": 0,
"NEXT_ON_TIME": "7 days 00:00",
"OFFLINE": false,
"OUPUT_DELAY": false,
"OUTPUT_DELAY": 0,
"PREHEAT": false,
"PREHEAT_TIME": "255:255",
"PROGRAM_MODE": "24HOURSFIXED",
"PUMP_DELAY": false,
"RADIATORS_OR_UNDERFLOOR": false,
"SENSOR_SELECTION": "BUILT_IN_AND_FLOOR",
"SET_COUNTDOWN_TIME": 0,
"STANDBY": false,
"STAT_MODE": {
"4_HEAT_LEVELS": true,
"MANUAL_OFF": true,
"THERMOSTAT": true
},
"TEMPERATURE_FORMAT": false,
"TEMP_HOLD": false,
"TIMECLOCK_MODE": false,
"TIMER": false,
"TIME_CLOCK_OVERIDE_BIT": false,
"ULTRA_VERSION": 0,
"VERSION_NUMBER": 20,
"WRITE_COUNT": 0,
"ZONE_1PAIRED_TO_MULTILINK": true,
"ZONE_1_OR_2": false,
"ZONE_2_PAIRED_TO_MULTILINK": false,
"device": "Hallway"
},
{
"AWAY": false,
"COOLING": false,
"COOLING_ENABLED": false,
"COOLING_TEMPERATURE_IN_WHOLE_DEGREES": 23,
"COOL_INP": false,
"COUNT_DOWN_TIME": "0:00",
"CRADLE_PAIRED_TO_REMOTE_SENSOR": false,
"CRADLE_PAIRED_TO_STAT": false,
"CURRENT_FLOOR_TEMPERATURE": 127,
"CURRENT_SET_TEMPERATURE": "10.0",
"CURRENT_TEMPERATURE": "17.3",
"DEMAND": false,
"DEVICE_TYPE": 12,
"ENABLE_BOILER": false,
"ENABLE_COOLING": false,
"ENABLE_PUMP": false,
"ENABLE_VALVE": false,
"ENABLE_ZONE": false,
"FAILSAFE_STATE": false,
"FAIL_SAFE_ENABLED": false,
"FLOOR_LIMIT": false,
"FULL/PARTIAL_LOCK_AVAILABLE": false,
"HEAT/COOL_MODE": false,
"HEATING": false,
"HOLD_TEMPERATURE": 10,
"HOLD_TIME": "0:00",
"HOLIDAY": false,
"HOLIDAY_DAYS": 0,
"HUMIDITY": 0,
"LOCK": false,
"LOCK_PIN_NUMBER": "0000",
"LOW_BATTERY": false,
"MAX_TEMPERATURE": "19.0",
"MIN_TEMPERATURE": "16.0",
"MODULATION_LEVEL": 0,
"NEXT_ON_TIME": "7 days 08:00",
"OFFLINE": false,
"OUPUT_DELAY": false,
"OUTPUT_DELAY": 5,
"PREHEAT": false,
"PREHEAT_TIME": "255:255",
"PROGRAM_MODE": "24HOURSFIXED",
"PUMP_DELAY": false,
"RADIATORS_OR_UNDERFLOOR": false,
"SENSOR_SELECTION": "BUILT_IN_AIR_SENSOR",
"SET_COUNTDOWN_TIME": 0,
"STANDBY": false,
"STAT_MODE": {
"4_HEAT_LEVELS": true,
"MANUAL_OFF": true,
"THERMOSTAT": true
},
"TEMPERATURE_FORMAT": false,
"TEMP_HOLD": false,
"TIMECLOCK_MODE": false,
"TIMER": false,
"TIME_CLOCK_OVERIDE_BIT": false,
"ULTRA_VERSION": 0,
"VERSION_NUMBER": 20,
"WRITE_COUNT": 0,
"ZONE_1PAIRED_TO_MULTILINK": true,
"ZONE_1_OR_2": false,
"ZONE_2_PAIRED_TO_MULTILINK": false,
"device": "Shed"
},
{
"AWAY": false,
"COOLING": false,
"COOLING_ENABLED": false,
"COOLING_TEMPERATURE_IN_WHOLE_DEGREES": 0,
"COOL_INP": false,
"COUNT_DOWN_TIME": "0:00",
"CRADLE_PAIRED_TO_REMOTE_SENSOR": false,
"CRADLE_PAIRED_TO_STAT": false,
"CURRENT_FLOOR_TEMPERATURE": 127,
"CURRENT_SET_TEMPERATURE": "0.0",
"CURRENT_TEMPERATURE": "255.255",
"DEMAND": false,
"DEVICE_TYPE": 6,
"ENABLE_BOILER": false,
"ENABLE_COOLING": false,
"ENABLE_PUMP": false,
"ENABLE_VALVE": false,
"ENABLE_ZONE": false,
"FAILSAFE_STATE": false,
"FAIL_SAFE_ENABLED": false,
"FLOOR_LIMIT": false,
"FULL/PARTIAL_LOCK_AVAILABLE": false,
"HEAT/COOL_MODE": false,
"HEATING": false,
"HOLD_TEMPERATURE": 1,
"HOLD_TIME": "0:00",
"HOLIDAY": false,
"HOLIDAY_DAYS": 0,
"HUMIDITY": 0,
"LOCK": false,
"LOCK_PIN_NUMBER": "0000",
"LOW_BATTERY": false,
"MAX_TEMPERATURE": "255.255",
"MIN_TEMPERATURE": "255.255",
"MODULATION_LEVEL": 0,
"NEXT_ON_TIME": "255 days 255:255",
"OFFLINE": false,
"OUPUT_DELAY": false,
"OUTPUT_DELAY": 0,
"PREHEAT": false,
"PREHEAT_TIME": "255:255",
"PROGRAM_MODE": "24HOURSFIXED",
"PUMP_DELAY": false,
"RADIATORS_OR_UNDERFLOOR": false,
"SENSOR_SELECTION": "BUILT_IN_AIR_SENSOR",
"SET_COUNTDOWN_TIME": 0,
"STANDBY": false,
"STAT_MODE": {
"4_HEAT_LEVELS": true,
"MANUAL_ON": true,
"TIMECLOCK": true
},
"TEMPERATURE_FORMAT": false,
"TEMP_HOLD": false,
"TIMECLOCK_MODE": false,
"TIMER": false,
"TIME_CLOCK_OVERIDE_BIT": false,
"ULTRA_VERSION": 0,
"VERSION_NUMBER": 79,
"WRITE_COUNT": 11,
"ZONE_1PAIRED_TO_MULTILINK": true,
"ZONE_1_OR_2": false,
"ZONE_2_PAIRED_TO_MULTILINK": false,
"device": "Plug South"
},
{
"AWAY": false,
"COOLING": false,
"COOLING_ENABLED": false,
"COOLING_TEMPERATURE_IN_WHOLE_DEGREES": 0,
"COOL_INP": false,
"COUNT_DOWN_TIME": "0:00",
"CRADLE_PAIRED_TO_REMOTE_SENSOR": false,
"CRADLE_PAIRED_TO_STAT": false,
"CURRENT_FLOOR_TEMPERATURE": 127,
"CURRENT_SET_TEMPERATURE": "0.0",
"CURRENT_TEMPERATURE": "255.255",
"DEMAND": false,
"DEVICE_TYPE": 6,
"ENABLE_BOILER": false,
"ENABLE_COOLING": false,
"ENABLE_PUMP": false,
"ENABLE_VALVE": false,
"ENABLE_ZONE": false,
"FAILSAFE_STATE": false,
"FAIL_SAFE_ENABLED": false,
"FLOOR_LIMIT": false,
"FULL/PARTIAL_LOCK_AVAILABLE": false,
"HEAT/COOL_MODE": false,
"HEATING": false,
"HOLD_TEMPERATURE": 0,
"HOLD_TIME": "0:00",
"HOLIDAY": false,
"HOLIDAY_DAYS": 0,
"HUMIDITY": 0,
"LOCK": false,
"LOCK_PIN_NUMBER": "0000",
"LOW_BATTERY": false,
"MAX_TEMPERATURE": "255.255",
"MIN_TEMPERATURE": "255.255",
"MODULATION_LEVEL": 0,
"NEXT_ON_TIME": "255 days 255:255",
"OFFLINE": false,
"OUPUT_DELAY": false,
"OUTPUT_DELAY": 0,
"PREHEAT": false,
"PREHEAT_TIME": "255:255",
"PROGRAM_MODE": "24HOURSFIXED",
"PUMP_DELAY": false,
"RADIATORS_OR_UNDERFLOOR": false,
"SENSOR_SELECTION": "BUILT_IN_AIR_SENSOR",
"SET_COUNTDOWN_TIME": 0,
"STANDBY": false,
"STAT_MODE": {
"4_HEAT_LEVELS": true,
"MANUAL_ON": true,
"TIMECLOCK": true
},
"TEMPERATURE_FORMAT": false,
"TEMP_HOLD": false,
"TIMECLOCK_MODE": false,
"TIMER": true,
"TIME_CLOCK_OVERIDE_BIT": true,
"ULTRA_VERSION": 0,
"VERSION_NUMBER": 79,
"WRITE_COUNT": 7,
"ZONE_1PAIRED_TO_MULTILINK": true,
"ZONE_1_OR_2": false,
"ZONE_2_PAIRED_TO_MULTILINK": false,
"device": "Plug North"
},
{
"AWAY": false,
"COOLING": false,
"COOLING_ENABLED": false,
"COOLING_TEMPERATURE_IN_WHOLE_DEGREES": 0,
"COOL_INP": false,
"COUNT_DOWN_TIME": "0:00",
"CRADLE_PAIRED_TO_REMOTE_SENSOR": false,
"CRADLE_PAIRED_TO_STAT": false,
"CURRENT_FLOOR_TEMPERATURE": 127,
"CURRENT_SET_TEMPERATURE": "0.0",
"CURRENT_TEMPERATURE": "255.255",
"DEMAND": false,
"DEVICE_TYPE": 6,
"ENABLE_BOILER": false,
"ENABLE_COOLING": false,
"ENABLE_PUMP": false,
"ENABLE_VALVE": false,
"ENABLE_ZONE": false,
"FAILSAFE_STATE": false,
"FAIL_SAFE_ENABLED": false,
"FLOOR_LIMIT": false,
"FULL/PARTIAL_LOCK_AVAILABLE": false,
"HEAT/COOL_MODE": false,
"HEATING": false,
"HOLD_TEMPERATURE": 1,
"HOLD_TIME": "0:00",
"HOLIDAY": false,
"HOLIDAY_DAYS": 0,
"HUMIDITY": 0,
"LOCK": false,
"LOCK_PIN_NUMBER": "0000",
"LOW_BATTERY": false,
"MAX_TEMPERATURE": "255.255",
"MIN_TEMPERATURE": "255.255",
"MODULATION_LEVEL": 0,
"NEXT_ON_TIME": "255 days 255:255",
"OFFLINE": false,
"OUPUT_DELAY": false,
"OUTPUT_DELAY": 0,
"PREHEAT": false,
"PREHEAT_TIME": "255:255",
"PROGRAM_MODE": "24HOURSFIXED",
"PUMP_DELAY": false,
"RADIATORS_OR_UNDERFLOOR": false,
"SENSOR_SELECTION": "BUILT_IN_AIR_SENSOR",
"SET_COUNTDOWN_TIME": 0,
"STANDBY": false,
"STAT_MODE": {
"4_HEAT_LEVELS": true,
"MANUAL_OFF": true,
"TIMECLOCK": true
},
"TEMPERATURE_FORMAT": false,
"TEMP_HOLD": false,
"TIMECLOCK_MODE": false,
"TIMER": false,
"TIME_CLOCK_OVERIDE_BIT": false,
"ULTRA_VERSION": 0,
"VERSION_NUMBER": 79,
"WRITE_COUNT": 28,
"ZONE_1PAIRED_TO_MULTILINK": true,
"ZONE_1_OR_2": false,
"ZONE_2_PAIRED_TO_MULTILINK": false,
"device": "Watering System"
}
]
}

View File

@@ -0,0 +1,466 @@
{
"devices": [
{
"AWAY": false,
"COOLING": false,
"COOLING_ENABLED": false,
"COOLING_TEMPERATURE_IN_WHOLE_DEGREES": 0,
"COOL_INP": false,
"COUNT_DOWN_TIME": "0:00",
"CRADLE_PAIRED_TO_REMOTE_SENSOR": false,
"CRADLE_PAIRED_TO_STAT": false,
"CURRENT_FLOOR_TEMPERATURE": 20,
"CURRENT_SET_TEMPERATURE": "20.0",
"CURRENT_TEMPERATURE": "19.8",
"DEMAND": false,
"DEVICE_TYPE": 1,
"ENABLE_BOILER": false,
"ENABLE_COOLING": false,
"ENABLE_PUMP": false,
"ENABLE_VALVE": false,
"ENABLE_ZONE": false,
"FAILSAFE_STATE": false,
"FAIL_SAFE_ENABLED": false,
"FLOOR_LIMIT": false,
"FULL/PARTIAL_LOCK_AVAILABLE": false,
"HEAT/COOL_MODE": false,
"HEATING": false,
"HOLD_TEMPERATURE": 12,
"HOLD_TIME": "0:00",
"HOLIDAY": false,
"HOLIDAY_DAYS": 0,
"HUMIDITY": 0,
"LOCK": false,
"LOCK_PIN_NUMBER": "0000",
"LOW_BATTERY": false,
"MAX_TEMPERATURE": "20.0",
"MIN_TEMPERATURE": "19.0",
"MODULATION_LEVEL": 0,
"NEXT_ON_TIME": "5 days 19:00",
"OFFLINE": 0,
"OUPUT_DELAY": false,
"OUTPUT_DELAY": 0,
"PREHEAT": false,
"PREHEAT_TIME": "5:13",
"PROGRAM_MODE": "5DAY/2DAY",
"PUMP_DELAY": false,
"RADIATORS_OR_UNDERFLOOR": false,
"SENSOR_SELECTION": "FLOOR_SENSOR_ONLY",
"SET_COUNTDOWN_TIME": 0,
"STANDBY": false,
"STAT_MODE": {
"4_HEAT_LEVELS": true,
"MANUAL_OFF": true,
"THERMOSTAT": true
},
"TEMPERATURE_FORMAT": false,
"TEMP_HOLD": false,
"TIMECLOCK_MODE": false,
"TIMER": false,
"TIME_CLOCK_OVERIDE_BIT": false,
"ULTRA_VERSION": 0,
"VERSION_NUMBER": 108,
"WRITE_COUNT": 89,
"ZONE_1PAIRED_TO_MULTILINK": true,
"ZONE_1_OR_2": false,
"ZONE_2_PAIRED_TO_MULTILINK": false,
"device": "Living Room Floor"
},
{
"AWAY": false,
"COOLING": false,
"COOLING_ENABLED": false,
"COOLING_TEMPERATURE_IN_WHOLE_DEGREES": 0,
"COOL_INP": false,
"COUNT_DOWN_TIME": "0:00",
"CRADLE_PAIRED_TO_REMOTE_SENSOR": false,
"CRADLE_PAIRED_TO_STAT": false,
"CURRENT_FLOOR_TEMPERATURE": 0,
"CURRENT_SET_TEMPERATURE": "0.0",
"CURRENT_TEMPERATURE": "19.5",
"DEMAND": false,
"DEVICE_TYPE": 14,
"ENABLE_BOILER": false,
"ENABLE_COOLING": false,
"ENABLE_PUMP": false,
"ENABLE_VALVE": false,
"ENABLE_ZONE": false,
"FAILSAFE_STATE": false,
"FAIL_SAFE_ENABLED": false,
"FLOOR_LIMIT": false,
"FULL/PARTIAL_LOCK_AVAILABLE": false,
"HEAT/COOL_MODE": false,
"HEATING": false,
"HOLD_TEMPERATURE": 0,
"HOLD_TIME": "0:00",
"HOLIDAY": false,
"HOLIDAY_DAYS": 0,
"HUMIDITY": 0,
"LOCK": false,
"LOCK_PIN_NUMBER": "0000",
"LOW_BATTERY": false,
"MAX_TEMPERATURE": "0.0",
"MIN_TEMPERATURE": "0.0",
"MODULATION_LEVEL": 0,
"NEXT_ON_TIME": "0 days 00:00",
"OFFLINE": 0,
"OUPUT_DELAY": false,
"OUTPUT_DELAY": 0,
"PREHEAT": false,
"PREHEAT_TIME": "0:00",
"PROGRAM_MODE": "NONPROGRAMMABLE",
"PUMP_DELAY": false,
"RADIATORS_OR_UNDERFLOOR": false,
"SENSOR_SELECTION": "BUILT_IN_AIR_SENSOR",
"SET_COUNTDOWN_TIME": 0,
"STANDBY": false,
"STAT_MODE": {
"4_HEAT_LEVELS": true,
"MANUAL_OFF": true,
"THERMOSTAT": true
},
"TEMPERATURE_FORMAT": false,
"TEMP_HOLD": false,
"TIMECLOCK_MODE": false,
"TIMER": false,
"TIME_CLOCK_OVERIDE_BIT": false,
"ULTRA_VERSION": 0,
"VERSION_NUMBER": 0,
"WRITE_COUNT": 0,
"ZONE_1PAIRED_TO_MULTILINK": false,
"ZONE_1_OR_2": false,
"ZONE_2_PAIRED_TO_MULTILINK": false,
"device": "Master Bedroom"
},
{
"AWAY": false,
"COOLING": false,
"COOLING_ENABLED": false,
"COOLING_TEMPERATURE_IN_WHOLE_DEGREES": 0,
"COOL_INP": false,
"COUNT_DOWN_TIME": "0:00",
"CRADLE_PAIRED_TO_REMOTE_SENSOR": false,
"CRADLE_PAIRED_TO_STAT": false,
"CURRENT_FLOOR_TEMPERATURE": 22,
"CURRENT_SET_TEMPERATURE": "12.0",
"CURRENT_TEMPERATURE": "21.5",
"DEMAND": false,
"DEVICE_TYPE": 1,
"ENABLE_BOILER": false,
"ENABLE_COOLING": false,
"ENABLE_PUMP": false,
"ENABLE_VALVE": false,
"ENABLE_ZONE": false,
"FAILSAFE_STATE": false,
"FAIL_SAFE_ENABLED": false,
"FLOOR_LIMIT": false,
"FULL/PARTIAL_LOCK_AVAILABLE": false,
"HEAT/COOL_MODE": false,
"HEATING": false,
"HOLD_TEMPERATURE": 12,
"HOLD_TIME": "0:00",
"HOLIDAY": false,
"HOLIDAY_DAYS": 0,
"HUMIDITY": 0,
"LOCK": false,
"LOCK_PIN_NUMBER": "1717",
"LOW_BATTERY": false,
"MAX_TEMPERATURE": "23.0",
"MIN_TEMPERATURE": "22.0",
"MODULATION_LEVEL": 0,
"NEXT_ON_TIME": "5 days 23:55",
"OFFLINE": 0,
"OUPUT_DELAY": false,
"OUTPUT_DELAY": 0,
"PREHEAT": false,
"PREHEAT_TIME": "255:255",
"PROGRAM_MODE": "5DAY/2DAY",
"PUMP_DELAY": false,
"RADIATORS_OR_UNDERFLOOR": false,
"SENSOR_SELECTION": "FLOOR_SENSOR_ONLY",
"SET_COUNTDOWN_TIME": 0,
"STANDBY": false,
"STAT_MODE": {
"4_HEAT_LEVELS": true,
"MANUAL_OFF": true,
"THERMOSTAT": true
},
"TEMPERATURE_FORMAT": false,
"TEMP_HOLD": false,
"TIMECLOCK_MODE": false,
"TIMER": false,
"TIME_CLOCK_OVERIDE_BIT": false,
"ULTRA_VERSION": 0,
"VERSION_NUMBER": 108,
"WRITE_COUNT": 26,
"ZONE_1PAIRED_TO_MULTILINK": true,
"ZONE_1_OR_2": false,
"ZONE_2_PAIRED_TO_MULTILINK": false,
"device": "Bathroom Floor"
},
{
"AWAY": false,
"COOLING": false,
"COOLING_ENABLED": false,
"COOLING_TEMPERATURE_IN_WHOLE_DEGREES": 0,
"COOL_INP": false,
"COUNT_DOWN_TIME": "0:00",
"CRADLE_PAIRED_TO_REMOTE_SENSOR": false,
"CRADLE_PAIRED_TO_STAT": false,
"CURRENT_FLOOR_TEMPERATURE": 22,
"CURRENT_SET_TEMPERATURE": "12.0",
"CURRENT_TEMPERATURE": "21.6",
"DEMAND": false,
"DEVICE_TYPE": 1,
"ENABLE_BOILER": false,
"ENABLE_COOLING": false,
"ENABLE_PUMP": false,
"ENABLE_VALVE": false,
"ENABLE_ZONE": false,
"FAILSAFE_STATE": false,
"FAIL_SAFE_ENABLED": false,
"FLOOR_LIMIT": false,
"FULL/PARTIAL_LOCK_AVAILABLE": false,
"HEAT/COOL_MODE": false,
"HEATING": false,
"HOLD_TEMPERATURE": 24,
"HOLD_TIME": "0:00",
"HOLIDAY": false,
"HOLIDAY_DAYS": 0,
"HUMIDITY": 0,
"LOCK": false,
"LOCK_PIN_NUMBER": "0000",
"LOW_BATTERY": false,
"MAX_TEMPERATURE": "23.0",
"MIN_TEMPERATURE": "21.0",
"MODULATION_LEVEL": 0,
"NEXT_ON_TIME": "5 days 22:30",
"OFFLINE": 0,
"OUPUT_DELAY": false,
"OUTPUT_DELAY": 0,
"PREHEAT": false,
"PREHEAT_TIME": "1:02",
"PROGRAM_MODE": "5DAY/2DAY",
"PUMP_DELAY": false,
"RADIATORS_OR_UNDERFLOOR": false,
"SENSOR_SELECTION": "FLOOR_SENSOR_ONLY",
"SET_COUNTDOWN_TIME": 0,
"STANDBY": false,
"STAT_MODE": {
"4_HEAT_LEVELS": true,
"MANUAL_OFF": true,
"THERMOSTAT": true
},
"TEMPERATURE_FORMAT": false,
"TEMP_HOLD": false,
"TIMECLOCK_MODE": false,
"TIMER": false,
"TIME_CLOCK_OVERIDE_BIT": false,
"ULTRA_VERSION": 0,
"VERSION_NUMBER": 108,
"WRITE_COUNT": 27,
"ZONE_1PAIRED_TO_MULTILINK": true,
"ZONE_1_OR_2": false,
"ZONE_2_PAIRED_TO_MULTILINK": false,
"device": "Ensuite Floor"
},
{
"AWAY": false,
"COOLING": false,
"COOLING_ENABLED": false,
"COOLING_TEMPERATURE_IN_WHOLE_DEGREES": 0,
"COOL_INP": false,
"COUNT_DOWN_TIME": "0:00",
"CRADLE_PAIRED_TO_REMOTE_SENSOR": false,
"CRADLE_PAIRED_TO_STAT": false,
"CURRENT_FLOOR_TEMPERATURE": 0,
"CURRENT_SET_TEMPERATURE": "0.0",
"CURRENT_TEMPERATURE": "20.0",
"DEMAND": false,
"DEVICE_TYPE": 14,
"ENABLE_BOILER": false,
"ENABLE_COOLING": false,
"ENABLE_PUMP": false,
"ENABLE_VALVE": false,
"ENABLE_ZONE": false,
"FAILSAFE_STATE": false,
"FAIL_SAFE_ENABLED": false,
"FLOOR_LIMIT": false,
"FULL/PARTIAL_LOCK_AVAILABLE": false,
"HEAT/COOL_MODE": false,
"HEATING": false,
"HOLD_TEMPERATURE": 0,
"HOLD_TIME": "0:00",
"HOLIDAY": false,
"HOLIDAY_DAYS": 0,
"HUMIDITY": 0,
"LOCK": false,
"LOCK_PIN_NUMBER": "0000",
"LOW_BATTERY": false,
"MAX_TEMPERATURE": "0.0",
"MIN_TEMPERATURE": "0.0",
"MODULATION_LEVEL": 0,
"NEXT_ON_TIME": "0 days 00:00",
"OFFLINE": 0,
"OUPUT_DELAY": false,
"OUTPUT_DELAY": 0,
"PREHEAT": false,
"PREHEAT_TIME": "0:00",
"PROGRAM_MODE": "NONPROGRAMMABLE",
"PUMP_DELAY": false,
"RADIATORS_OR_UNDERFLOOR": false,
"SENSOR_SELECTION": "BUILT_IN_AIR_SENSOR",
"SET_COUNTDOWN_TIME": 0,
"STANDBY": false,
"STAT_MODE": {
"4_HEAT_LEVELS": true,
"MANUAL_OFF": true,
"THERMOSTAT": true
},
"TEMPERATURE_FORMAT": false,
"TEMP_HOLD": false,
"TIMECLOCK_MODE": false,
"TIMER": false,
"TIME_CLOCK_OVERIDE_BIT": false,
"ULTRA_VERSION": 0,
"VERSION_NUMBER": 0,
"WRITE_COUNT": 0,
"ZONE_1PAIRED_TO_MULTILINK": false,
"ZONE_1_OR_2": false,
"ZONE_2_PAIRED_TO_MULTILINK": false,
"device": "Nursery"
},
{
"AWAY": false,
"COOLING": false,
"COOLING_ENABLED": false,
"COOLING_TEMPERATURE_IN_WHOLE_DEGREES": 0,
"COOL_INP": false,
"COUNT_DOWN_TIME": "0:00",
"CRADLE_PAIRED_TO_REMOTE_SENSOR": false,
"CRADLE_PAIRED_TO_STAT": false,
"CURRENT_FLOOR_TEMPERATURE": 0,
"CURRENT_SET_TEMPERATURE": "0.0",
"CURRENT_TEMPERATURE": "0.0",
"DEMAND": false,
"DEVICE_TYPE": 5,
"ENABLE_BOILER": false,
"ENABLE_COOLING": false,
"ENABLE_PUMP": false,
"ENABLE_VALVE": false,
"ENABLE_ZONE": false,
"FAILSAFE_STATE": false,
"FAIL_SAFE_ENABLED": false,
"FLOOR_LIMIT": false,
"FULL/PARTIAL_LOCK_AVAILABLE": false,
"HEAT/COOL_MODE": false,
"HEATING": false,
"HOLD_TEMPERATURE": 0,
"HOLD_TIME": "0:00",
"HOLIDAY": false,
"HOLIDAY_DAYS": 0,
"HUMIDITY": 0,
"LOCK": false,
"LOCK_PIN_NUMBER": "0000",
"LOW_BATTERY": false,
"MAX_TEMPERATURE": "0.0",
"MIN_TEMPERATURE": "0.0",
"MODULATION_LEVEL": 0,
"NEXT_ON_TIME": "0 days 00:00",
"OFFLINE": 0,
"OUPUT_DELAY": false,
"OUTPUT_DELAY": 0,
"PREHEAT": false,
"PREHEAT_TIME": "0:00",
"PROGRAM_MODE": "NONPROGRAMMABLE",
"PUMP_DELAY": false,
"RADIATORS_OR_UNDERFLOOR": false,
"SENSOR_SELECTION": "BUILT_IN_AIR_SENSOR",
"SET_COUNTDOWN_TIME": 0,
"STANDBY": false,
"STAT_MODE": {
"4_HEAT_LEVELS": true,
"MANUAL_OFF": true,
"TIMECLOCK": true
},
"TEMPERATURE_FORMAT": false,
"TEMP_HOLD": false,
"TIMECLOCK_MODE": false,
"TIMER": false,
"TIME_CLOCK_OVERIDE_BIT": false,
"ULTRA_VERSION": 0,
"VERSION_NUMBER": 0,
"WRITE_COUNT": 0,
"ZONE_1PAIRED_TO_MULTILINK": false,
"ZONE_1_OR_2": false,
"ZONE_2_PAIRED_TO_MULTILINK": false,
"device": "Back Door"
},
{
"AWAY": false,
"COOLING": false,
"COOLING_ENABLED": false,
"COOLING_TEMPERATURE_IN_WHOLE_DEGREES": 0,
"COOL_INP": false,
"COUNT_DOWN_TIME": "0:00",
"CRADLE_PAIRED_TO_REMOTE_SENSOR": false,
"CRADLE_PAIRED_TO_STAT": false,
"CURRENT_FLOOR_TEMPERATURE": 0,
"CURRENT_SET_TEMPERATURE": "0.0",
"CURRENT_TEMPERATURE": "0.0",
"DEMAND": false,
"DEVICE_TYPE": 5,
"ENABLE_BOILER": false,
"ENABLE_COOLING": false,
"ENABLE_PUMP": false,
"ENABLE_VALVE": false,
"ENABLE_ZONE": false,
"FAILSAFE_STATE": false,
"FAIL_SAFE_ENABLED": false,
"FLOOR_LIMIT": false,
"FULL/PARTIAL_LOCK_AVAILABLE": false,
"HEAT/COOL_MODE": false,
"HEATING": false,
"HOLD_TEMPERATURE": 0,
"HOLD_TIME": "0:00",
"HOLIDAY": false,
"HOLIDAY_DAYS": 0,
"HUMIDITY": 0,
"LOCK": false,
"LOCK_PIN_NUMBER": "0000",
"LOW_BATTERY": false,
"MAX_TEMPERATURE": "0.0",
"MIN_TEMPERATURE": "0.0",
"MODULATION_LEVEL": 0,
"NEXT_ON_TIME": "0 days 00:00",
"OFFLINE": 0,
"OUPUT_DELAY": false,
"OUTPUT_DELAY": 0,
"PREHEAT": false,
"PREHEAT_TIME": "0:00",
"PROGRAM_MODE": "NONPROGRAMMABLE",
"PUMP_DELAY": false,
"RADIATORS_OR_UNDERFLOOR": false,
"SENSOR_SELECTION": "BUILT_IN_AIR_SENSOR",
"SET_COUNTDOWN_TIME": 0,
"STANDBY": false,
"STAT_MODE": {
"4_HEAT_LEVELS": true,
"MANUAL_OFF": true,
"TIMECLOCK": true
},
"TEMPERATURE_FORMAT": false,
"TEMP_HOLD": false,
"TIMECLOCK_MODE": false,
"TIMER": false,
"TIME_CLOCK_OVERIDE_BIT": false,
"ULTRA_VERSION": 0,
"VERSION_NUMBER": 0,
"WRITE_COUNT": 0,
"ZONE_1PAIRED_TO_MULTILINK": false,
"ZONE_1_OR_2": false,
"ZONE_2_PAIRED_TO_MULTILINK": false,
"device": "Nursery Window"
}
]
}

View File

@@ -0,0 +1,466 @@
{
"devices": [
{
"AWAY": false,
"COOLING": false,
"COOLING_ENABLED": false,
"COOLING_TEMPERATURE_IN_WHOLE_DEGREES": 0,
"COOL_INP": false,
"COUNT_DOWN_TIME": "0:00",
"CRADLE_PAIRED_TO_REMOTE_SENSOR": false,
"CRADLE_PAIRED_TO_STAT": false,
"CURRENT_FLOOR_TEMPERATURE": 20,
"CURRENT_SET_TEMPERATURE": "7.0",
"CURRENT_TEMPERATURE": "19.8",
"DEMAND": false,
"DEVICE_TYPE": 1,
"ENABLE_BOILER": false,
"ENABLE_COOLING": false,
"ENABLE_PUMP": false,
"ENABLE_VALVE": false,
"ENABLE_ZONE": false,
"FAILSAFE_STATE": false,
"FAIL_SAFE_ENABLED": false,
"FLOOR_LIMIT": false,
"FULL/PARTIAL_LOCK_AVAILABLE": false,
"HEAT/COOL_MODE": false,
"HEATING": false,
"HOLD_TEMPERATURE": 12,
"HOLD_TIME": "0:00",
"HOLIDAY": false,
"HOLIDAY_DAYS": 0,
"HUMIDITY": 0,
"LOCK": false,
"LOCK_PIN_NUMBER": "0000",
"LOW_BATTERY": false,
"MAX_TEMPERATURE": "20.0",
"MIN_TEMPERATURE": "19.0",
"MODULATION_LEVEL": 0,
"NEXT_ON_TIME": "255 days 255:255",
"OFFLINE": 0,
"OUPUT_DELAY": false,
"OUTPUT_DELAY": 0,
"PREHEAT": false,
"PREHEAT_TIME": "5:13",
"PROGRAM_MODE": "5DAY/2DAY",
"PUMP_DELAY": false,
"RADIATORS_OR_UNDERFLOOR": false,
"SENSOR_SELECTION": "FLOOR_SENSOR_ONLY",
"SET_COUNTDOWN_TIME": 0,
"STANDBY": true,
"STAT_MODE": {
"4_HEAT_LEVELS": true,
"MANUAL_OFF": true,
"THERMOSTAT": true
},
"TEMPERATURE_FORMAT": false,
"TEMP_HOLD": false,
"TIMECLOCK_MODE": false,
"TIMER": false,
"TIME_CLOCK_OVERIDE_BIT": false,
"ULTRA_VERSION": 0,
"VERSION_NUMBER": 108,
"WRITE_COUNT": 90,
"ZONE_1PAIRED_TO_MULTILINK": true,
"ZONE_1_OR_2": false,
"ZONE_2_PAIRED_TO_MULTILINK": false,
"device": "Living Room Floor"
},
{
"AWAY": false,
"COOLING": false,
"COOLING_ENABLED": false,
"COOLING_TEMPERATURE_IN_WHOLE_DEGREES": 0,
"COOL_INP": false,
"COUNT_DOWN_TIME": "0:00",
"CRADLE_PAIRED_TO_REMOTE_SENSOR": false,
"CRADLE_PAIRED_TO_STAT": false,
"CURRENT_FLOOR_TEMPERATURE": 0,
"CURRENT_SET_TEMPERATURE": "0.0",
"CURRENT_TEMPERATURE": "19.5",
"DEMAND": false,
"DEVICE_TYPE": 14,
"ENABLE_BOILER": false,
"ENABLE_COOLING": false,
"ENABLE_PUMP": false,
"ENABLE_VALVE": false,
"ENABLE_ZONE": false,
"FAILSAFE_STATE": false,
"FAIL_SAFE_ENABLED": false,
"FLOOR_LIMIT": false,
"FULL/PARTIAL_LOCK_AVAILABLE": false,
"HEAT/COOL_MODE": false,
"HEATING": false,
"HOLD_TEMPERATURE": 0,
"HOLD_TIME": "0:00",
"HOLIDAY": false,
"HOLIDAY_DAYS": 0,
"HUMIDITY": 0,
"LOCK": false,
"LOCK_PIN_NUMBER": "0000",
"LOW_BATTERY": false,
"MAX_TEMPERATURE": "0.0",
"MIN_TEMPERATURE": "0.0",
"MODULATION_LEVEL": 0,
"NEXT_ON_TIME": "0 days 00:00",
"OFFLINE": 0,
"OUPUT_DELAY": false,
"OUTPUT_DELAY": 0,
"PREHEAT": false,
"PREHEAT_TIME": "0:00",
"PROGRAM_MODE": "NONPROGRAMMABLE",
"PUMP_DELAY": false,
"RADIATORS_OR_UNDERFLOOR": false,
"SENSOR_SELECTION": "BUILT_IN_AIR_SENSOR",
"SET_COUNTDOWN_TIME": 0,
"STANDBY": false,
"STAT_MODE": {
"4_HEAT_LEVELS": true,
"MANUAL_OFF": true,
"THERMOSTAT": true
},
"TEMPERATURE_FORMAT": false,
"TEMP_HOLD": false,
"TIMECLOCK_MODE": false,
"TIMER": false,
"TIME_CLOCK_OVERIDE_BIT": false,
"ULTRA_VERSION": 0,
"VERSION_NUMBER": 0,
"WRITE_COUNT": 0,
"ZONE_1PAIRED_TO_MULTILINK": false,
"ZONE_1_OR_2": false,
"ZONE_2_PAIRED_TO_MULTILINK": false,
"device": "Master Bedroom"
},
{
"AWAY": false,
"COOLING": false,
"COOLING_ENABLED": false,
"COOLING_TEMPERATURE_IN_WHOLE_DEGREES": 0,
"COOL_INP": false,
"COUNT_DOWN_TIME": "0:00",
"CRADLE_PAIRED_TO_REMOTE_SENSOR": false,
"CRADLE_PAIRED_TO_STAT": false,
"CURRENT_FLOOR_TEMPERATURE": 22,
"CURRENT_SET_TEMPERATURE": "12.0",
"CURRENT_TEMPERATURE": "21.6",
"DEMAND": false,
"DEVICE_TYPE": 1,
"ENABLE_BOILER": false,
"ENABLE_COOLING": false,
"ENABLE_PUMP": false,
"ENABLE_VALVE": false,
"ENABLE_ZONE": false,
"FAILSAFE_STATE": false,
"FAIL_SAFE_ENABLED": false,
"FLOOR_LIMIT": false,
"FULL/PARTIAL_LOCK_AVAILABLE": false,
"HEAT/COOL_MODE": false,
"HEATING": false,
"HOLD_TEMPERATURE": 12,
"HOLD_TIME": "0:00",
"HOLIDAY": false,
"HOLIDAY_DAYS": 0,
"HUMIDITY": 0,
"LOCK": false,
"LOCK_PIN_NUMBER": "1717",
"LOW_BATTERY": false,
"MAX_TEMPERATURE": "23.0",
"MIN_TEMPERATURE": "22.0",
"MODULATION_LEVEL": 0,
"NEXT_ON_TIME": "5 days 23:55",
"OFFLINE": 0,
"OUPUT_DELAY": false,
"OUTPUT_DELAY": 0,
"PREHEAT": false,
"PREHEAT_TIME": "255:255",
"PROGRAM_MODE": "5DAY/2DAY",
"PUMP_DELAY": false,
"RADIATORS_OR_UNDERFLOOR": false,
"SENSOR_SELECTION": "FLOOR_SENSOR_ONLY",
"SET_COUNTDOWN_TIME": 0,
"STANDBY": false,
"STAT_MODE": {
"4_HEAT_LEVELS": true,
"MANUAL_OFF": true,
"THERMOSTAT": true
},
"TEMPERATURE_FORMAT": false,
"TEMP_HOLD": false,
"TIMECLOCK_MODE": false,
"TIMER": false,
"TIME_CLOCK_OVERIDE_BIT": false,
"ULTRA_VERSION": 0,
"VERSION_NUMBER": 108,
"WRITE_COUNT": 26,
"ZONE_1PAIRED_TO_MULTILINK": true,
"ZONE_1_OR_2": false,
"ZONE_2_PAIRED_TO_MULTILINK": false,
"device": "Bathroom Floor"
},
{
"AWAY": false,
"COOLING": false,
"COOLING_ENABLED": false,
"COOLING_TEMPERATURE_IN_WHOLE_DEGREES": 0,
"COOL_INP": false,
"COUNT_DOWN_TIME": "0:00",
"CRADLE_PAIRED_TO_REMOTE_SENSOR": false,
"CRADLE_PAIRED_TO_STAT": false,
"CURRENT_FLOOR_TEMPERATURE": 22,
"CURRENT_SET_TEMPERATURE": "12.0",
"CURRENT_TEMPERATURE": "21.6",
"DEMAND": false,
"DEVICE_TYPE": 1,
"ENABLE_BOILER": false,
"ENABLE_COOLING": false,
"ENABLE_PUMP": false,
"ENABLE_VALVE": false,
"ENABLE_ZONE": false,
"FAILSAFE_STATE": false,
"FAIL_SAFE_ENABLED": false,
"FLOOR_LIMIT": false,
"FULL/PARTIAL_LOCK_AVAILABLE": false,
"HEAT/COOL_MODE": false,
"HEATING": false,
"HOLD_TEMPERATURE": 24,
"HOLD_TIME": "0:00",
"HOLIDAY": false,
"HOLIDAY_DAYS": 0,
"HUMIDITY": 0,
"LOCK": false,
"LOCK_PIN_NUMBER": "0000",
"LOW_BATTERY": false,
"MAX_TEMPERATURE": "23.0",
"MIN_TEMPERATURE": "21.0",
"MODULATION_LEVEL": 0,
"NEXT_ON_TIME": "5 days 22:30",
"OFFLINE": 0,
"OUPUT_DELAY": false,
"OUTPUT_DELAY": 0,
"PREHEAT": false,
"PREHEAT_TIME": "1:02",
"PROGRAM_MODE": "5DAY/2DAY",
"PUMP_DELAY": false,
"RADIATORS_OR_UNDERFLOOR": false,
"SENSOR_SELECTION": "FLOOR_SENSOR_ONLY",
"SET_COUNTDOWN_TIME": 0,
"STANDBY": false,
"STAT_MODE": {
"4_HEAT_LEVELS": true,
"MANUAL_OFF": true,
"THERMOSTAT": true
},
"TEMPERATURE_FORMAT": false,
"TEMP_HOLD": false,
"TIMECLOCK_MODE": false,
"TIMER": false,
"TIME_CLOCK_OVERIDE_BIT": false,
"ULTRA_VERSION": 0,
"VERSION_NUMBER": 108,
"WRITE_COUNT": 27,
"ZONE_1PAIRED_TO_MULTILINK": true,
"ZONE_1_OR_2": false,
"ZONE_2_PAIRED_TO_MULTILINK": false,
"device": "Ensuite Floor"
},
{
"AWAY": false,
"COOLING": false,
"COOLING_ENABLED": false,
"COOLING_TEMPERATURE_IN_WHOLE_DEGREES": 0,
"COOL_INP": false,
"COUNT_DOWN_TIME": "0:00",
"CRADLE_PAIRED_TO_REMOTE_SENSOR": false,
"CRADLE_PAIRED_TO_STAT": false,
"CURRENT_FLOOR_TEMPERATURE": 0,
"CURRENT_SET_TEMPERATURE": "0.0",
"CURRENT_TEMPERATURE": "20.0",
"DEMAND": false,
"DEVICE_TYPE": 14,
"ENABLE_BOILER": false,
"ENABLE_COOLING": false,
"ENABLE_PUMP": false,
"ENABLE_VALVE": false,
"ENABLE_ZONE": false,
"FAILSAFE_STATE": false,
"FAIL_SAFE_ENABLED": false,
"FLOOR_LIMIT": false,
"FULL/PARTIAL_LOCK_AVAILABLE": false,
"HEAT/COOL_MODE": false,
"HEATING": false,
"HOLD_TEMPERATURE": 0,
"HOLD_TIME": "0:00",
"HOLIDAY": false,
"HOLIDAY_DAYS": 0,
"HUMIDITY": 0,
"LOCK": false,
"LOCK_PIN_NUMBER": "0000",
"LOW_BATTERY": false,
"MAX_TEMPERATURE": "0.0",
"MIN_TEMPERATURE": "0.0",
"MODULATION_LEVEL": 0,
"NEXT_ON_TIME": "0 days 00:00",
"OFFLINE": 0,
"OUPUT_DELAY": false,
"OUTPUT_DELAY": 0,
"PREHEAT": false,
"PREHEAT_TIME": "0:00",
"PROGRAM_MODE": "NONPROGRAMMABLE",
"PUMP_DELAY": false,
"RADIATORS_OR_UNDERFLOOR": false,
"SENSOR_SELECTION": "BUILT_IN_AIR_SENSOR",
"SET_COUNTDOWN_TIME": 0,
"STANDBY": false,
"STAT_MODE": {
"4_HEAT_LEVELS": true,
"MANUAL_OFF": true,
"THERMOSTAT": true
},
"TEMPERATURE_FORMAT": false,
"TEMP_HOLD": false,
"TIMECLOCK_MODE": false,
"TIMER": false,
"TIME_CLOCK_OVERIDE_BIT": false,
"ULTRA_VERSION": 0,
"VERSION_NUMBER": 0,
"WRITE_COUNT": 0,
"ZONE_1PAIRED_TO_MULTILINK": false,
"ZONE_1_OR_2": false,
"ZONE_2_PAIRED_TO_MULTILINK": false,
"device": "Nursery"
},
{
"AWAY": false,
"COOLING": false,
"COOLING_ENABLED": false,
"COOLING_TEMPERATURE_IN_WHOLE_DEGREES": 0,
"COOL_INP": true,
"COUNT_DOWN_TIME": "0:00",
"CRADLE_PAIRED_TO_REMOTE_SENSOR": false,
"CRADLE_PAIRED_TO_STAT": false,
"CURRENT_FLOOR_TEMPERATURE": 0,
"CURRENT_SET_TEMPERATURE": "0.0",
"CURRENT_TEMPERATURE": "0.0",
"DEMAND": false,
"DEVICE_TYPE": 5,
"ENABLE_BOILER": false,
"ENABLE_COOLING": false,
"ENABLE_PUMP": false,
"ENABLE_VALVE": false,
"ENABLE_ZONE": false,
"FAILSAFE_STATE": false,
"FAIL_SAFE_ENABLED": false,
"FLOOR_LIMIT": false,
"FULL/PARTIAL_LOCK_AVAILABLE": false,
"HEAT/COOL_MODE": false,
"HEATING": false,
"HOLD_TEMPERATURE": 0,
"HOLD_TIME": "0:00",
"HOLIDAY": false,
"HOLIDAY_DAYS": 0,
"HUMIDITY": 0,
"LOCK": false,
"LOCK_PIN_NUMBER": "0000",
"LOW_BATTERY": true,
"MAX_TEMPERATURE": "0.0",
"MIN_TEMPERATURE": "0.0",
"MODULATION_LEVEL": 0,
"NEXT_ON_TIME": "0 days 00:00",
"OFFLINE": 0,
"OUPUT_DELAY": false,
"OUTPUT_DELAY": 0,
"PREHEAT": false,
"PREHEAT_TIME": "0:00",
"PROGRAM_MODE": "NONPROGRAMMABLE",
"PUMP_DELAY": false,
"RADIATORS_OR_UNDERFLOOR": false,
"SENSOR_SELECTION": "BUILT_IN_AIR_SENSOR",
"SET_COUNTDOWN_TIME": 0,
"STANDBY": false,
"STAT_MODE": {
"4_HEAT_LEVELS": true,
"MANUAL_OFF": true,
"TIMECLOCK": true
},
"TEMPERATURE_FORMAT": false,
"TEMP_HOLD": false,
"TIMECLOCK_MODE": false,
"TIMER": false,
"TIME_CLOCK_OVERIDE_BIT": false,
"ULTRA_VERSION": 0,
"VERSION_NUMBER": 0,
"WRITE_COUNT": 0,
"ZONE_1PAIRED_TO_MULTILINK": false,
"ZONE_1_OR_2": false,
"ZONE_2_PAIRED_TO_MULTILINK": false,
"device": "Back Door"
},
{
"AWAY": false,
"COOLING": false,
"COOLING_ENABLED": false,
"COOLING_TEMPERATURE_IN_WHOLE_DEGREES": 0,
"COOL_INP": true,
"COUNT_DOWN_TIME": "0:00",
"CRADLE_PAIRED_TO_REMOTE_SENSOR": false,
"CRADLE_PAIRED_TO_STAT": false,
"CURRENT_FLOOR_TEMPERATURE": 0,
"CURRENT_SET_TEMPERATURE": "0.0",
"CURRENT_TEMPERATURE": "0.0",
"DEMAND": false,
"DEVICE_TYPE": 5,
"ENABLE_BOILER": false,
"ENABLE_COOLING": false,
"ENABLE_PUMP": false,
"ENABLE_VALVE": false,
"ENABLE_ZONE": false,
"FAILSAFE_STATE": false,
"FAIL_SAFE_ENABLED": false,
"FLOOR_LIMIT": false,
"FULL/PARTIAL_LOCK_AVAILABLE": false,
"HEAT/COOL_MODE": false,
"HEATING": false,
"HOLD_TEMPERATURE": 0,
"HOLD_TIME": "0:00",
"HOLIDAY": false,
"HOLIDAY_DAYS": 0,
"HUMIDITY": 0,
"LOCK": false,
"LOCK_PIN_NUMBER": "0000",
"LOW_BATTERY": true,
"MAX_TEMPERATURE": "0.0",
"MIN_TEMPERATURE": "0.0",
"MODULATION_LEVEL": 0,
"NEXT_ON_TIME": "0 days 00:00",
"OFFLINE": 0,
"OUPUT_DELAY": false,
"OUTPUT_DELAY": 0,
"PREHEAT": false,
"PREHEAT_TIME": "0:00",
"PROGRAM_MODE": "NONPROGRAMMABLE",
"PUMP_DELAY": false,
"RADIATORS_OR_UNDERFLOOR": false,
"SENSOR_SELECTION": "BUILT_IN_AIR_SENSOR",
"SET_COUNTDOWN_TIME": 0,
"STANDBY": false,
"STAT_MODE": {
"4_HEAT_LEVELS": true,
"MANUAL_OFF": true,
"TIMECLOCK": true
},
"TEMPERATURE_FORMAT": false,
"TEMP_HOLD": false,
"TIMECLOCK_MODE": false,
"TIMER": false,
"TIME_CLOCK_OVERIDE_BIT": false,
"ULTRA_VERSION": 0,
"VERSION_NUMBER": 0,
"WRITE_COUNT": 0,
"ZONE_1PAIRED_TO_MULTILINK": false,
"ZONE_1_OR_2": false,
"ZONE_2_PAIRED_TO_MULTILINK": false,
"device": "Nursery Window"
}
]
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,32 @@
{
"ALT_TIMER_FORMAT": 1,
"CORF": "C",
"DEVICE_ID": "NeoHub",
"DST_AUTO": true,
"DST_ON": true,
"EXTENDED_HISTORY": "off",
"FORMAT": 1,
"GDEVLIST": [
1,
2,
3,
4,
5,
6,
7,
8,
9,
10
],
"GLOBAL_HC_MODE": "heating",
"GLOBAL_SYSTEM_TYPE": "HeatOnly",
"HEATING_LEVELS": 4,
"HUB_TYPE": 2,
"HUB_VERSION": 2134,
"NTP_ON": "Running",
"PARTITION": "4",
"TIMESTAMP": 0,
"TIMEZONESTR": null,
"TIME_ZONE": 0.0,
"UTC": 1588513535
}