mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[warmup] Fixes & Enhancements (#16387)
* Update docs for multiple device support Signed-off-by: James Melville <jamesmelville@gmail.com> Signed-off-by: Ciprian Pascu <contact@ciprianpascu.ro>
This commit is contained in:
parent
f93ff0da03
commit
29d9e2effb
@ -1,11 +1,30 @@
|
||||
# Warmup Binding
|
||||
|
||||
This binding integrates the Warmup 4iE Thermostat <https://www.warmup.co.uk/thermostats/smart/4ie-underfloor-heating>, via the API at <https://my.warmup.com/>.
|
||||
This binding integrates [Warmup](https://www.warmup.co.uk) Wifi enabled Thermostats via the API at <https://my.warmup.com/>.
|
||||
|
||||
Any Warmup 4iE device(s) must be registered at <https://my.warmup.com/> prior to usage.
|
||||
Devices known to work with the binding:
|
||||
|
||||
* [Warmup 4iE](https://www.warmup.co.uk/thermostats/smart/4ie-underfloor-heating)
|
||||
* [Warmup Element](https://www.warmup.co.uk/thermostats/smart/element-wifi-thermostat)
|
||||
|
||||
Device expected to work with the binding:
|
||||
|
||||
* [Warmup 6iE](https://www.warmup.co.uk/thermostats/smart/6ie-underfloor-heating)
|
||||
|
||||
Devices which might work with the binding:
|
||||
|
||||
* Other similar looking devices marketed under different brands, mentioned in the API
|
||||
* [Laticrete](https://laticrete.com/)
|
||||
* [Rointe](https://rointe.com/)
|
||||
* [Porcelanosa](https://www.porcelanosa.com/)
|
||||
* Equus
|
||||
* [Savant](https://www.savant.com/)
|
||||
|
||||
Any Warmup device must be registered at <https://my.warmup.com/> prior to usage, or connected through the [MyHeating app](https://www.warmup.co.uk/thermostats/smart/myheating-app).
|
||||
|
||||
This API is not known to be documented publicly.
|
||||
The binding api implementation has been derived from the implementations at <https://github.com/alyc100/SmartThingsPublic/blob/master/devicetypes/alyc100/warmup-4ie.src/warmup-4ie.groovy> and <https://github.com/alex-0103/warmup4IE/blob/master/warmup4ie/warmup4ie.py>, and enhanced by inspecting the GraphQL endpoint.
|
||||
The binding api implementation has been derived from the implementations at <https://github.com/alyc100/SmartThingsPublic/blob/master/devicetypes/alyc100/warmup-4ie.src/warmup-4ie.groovy> and <https://github.com/alex-0103/warmup4IE/blob/master/warmup4ie/warmup4ie.py>, and enhanced by inspecting the [GraphQL endpoint](https://apil.warmup.com/graphql).
|
||||
|
||||
|
||||
## Supported Things
|
||||
|
||||
@ -15,15 +34,15 @@ The Warmup binding supports the following thing types:
|
||||
|----------------|-------------------|----------------------------------------------------------------------------------------|
|
||||
| `my-warmup` | My Warmup Account | The account credentials for my.warmup.com which acts as an API to the Warmup device(s) |
|
||||
|
||||
| Thing | Label | Description |
|
||||
|----------|-------|----------------------------------------------------------------------------------------------------------------------|
|
||||
| `room` | Room | A room containing an individual Warmup 4iE device which is a WiFi connected device which controls a heating circuit. |
|
||||
| Thing | Label | Description |
|
||||
|----------|-------|------------------------------------------------------------------------------------------------|
|
||||
| `room` | Room | A room containing an individual Warmup WiFi connected device which controls a heating circuit. |
|
||||
|
||||
### Room
|
||||
|
||||
The device is optimised for controlling underfloor heating (electric or hydronic), although it can also control central heating circuits.
|
||||
The device reports the temperature from one of two thermostats, either a floor temperature probe or the air temperature at the device.
|
||||
The separate temperatures do not appear to be reported through the API. It appears to be possible to configure two devices in a primary / secondary configuration, but it is not clear how this might be represented by the API and hasn't been implemented.
|
||||
It appears to be possible to configure two devices in a primary / secondary configuration, but it is not clear how this might be represented by the API and hasn't been implemented.
|
||||
|
||||
## Discovery
|
||||
|
||||
@ -41,12 +60,12 @@ Once credentials are successfully added to the bridge, any rooms (devices) detec
|
||||
|
||||
### Room
|
||||
|
||||
Rooms are configured automatically with a Serial Number on discovery, or can be added manually using the "Device Number" from the device, excluding the last 3 characters. The only supported temperature change is an override, through a default duration configured on the thing. This defaults to 60 minutes.
|
||||
Rooms are configured automatically with a Serial Number on discovery, or can be added manually using the "Device Number" from the device, excluding the last 3 characters. Changing the target temperature results in a temporary override to that temperature, for the duration configured on the thing. This defaults to 60 minutes.
|
||||
|
||||
| config parameter | type | description | required | default |
|
||||
|------------------|---------|--------------------------------------------------------------------|----------|---------|
|
||||
| serialNumber | String | Device Serial Number, excluding last 3 characters | true | |
|
||||
| overrideDuration | Integer | Duration in minutes of override when target temperature is changed | true | 60 |
|
||||
| overrideDuration | Integer | Duration in minutes of override when target temperature is changed | false | 60 |
|
||||
|
||||
## Channels
|
||||
|
||||
@ -55,12 +74,20 @@ Rooms are configured automatically with a Serial Number on discovery, or can be
|
||||
| currentTemperature | Number:Temperature | Currently reported temperature | true |
|
||||
| targetTemperature | Number:Temperature | Target temperature | false |
|
||||
| overrideRemaining | Number:Time | Duration remaining of the configured override | true |
|
||||
| runMode | String | Current operating mode of the thermostat, options listed below | true |
|
||||
| fixedTemperature | Number:Temperature | Target temperature for fixed mode | false |
|
||||
| energyToday | Number:Energy | Today's current energy consumption. | true |
|
||||
| runMode | String | Current operating mode of the thermostat, options listed below | false |
|
||||
| frostProtectionMode | Switch | Toggles between the "Frost Protection" run mode and the previously configured "active" run mode (known options are either Fixed or Schedule) | false |
|
||||
| airTemperature | Number:Temperature | Currently reported air temperature at the device | true |
|
||||
| floor1Temperature | Number:Temperature | Currently reported temperature from floor probe 1 on the device | true |
|
||||
| floor2Temperature | Number:Temperature | Currently reported temperature from floor probe 2 on the device | true |
|
||||
|
||||
### Run Mode Statuses
|
||||
|
||||
These run mode statuses are defined for the API. The descriptions are based on inspection of the device behaviour and are not sourced from documentation.
|
||||
These run mode statuses are defined for the API.
|
||||
The descriptions are based on inspection of the device behaviour and are not sourced from documentation.
|
||||
Only the value `schedule` is writeable, this reverts the device to the program/schedule configured on the device.
|
||||
The value `fixed` can be set by commanding the `fixedTemperature` channel. The value `override` can be set by commanding the `targetTemperature` channel.
|
||||
|
||||
| api value | ui name | description |
|
||||
|------------|------------------|---------------------------------------------------------------------------------|
|
||||
@ -76,6 +103,40 @@ These run mode statuses are defined for the API. The descriptions are based on i
|
||||
| relay | Relay | Unknown |
|
||||
| previous | Previous | Unknown |
|
||||
|
||||
## Rule Actions
|
||||
|
||||
### setOverride(temperature, duration)
|
||||
|
||||
Sets a temporary temperature override on the device
|
||||
|
||||
Parameters:
|
||||
|
||||
| Name | Type | Description |
|
||||
|-------------|---------------------------|-------------------------------------------------------------------------|
|
||||
| temperature | QuantityType<Temperature> | Override temperature. Must be between 5°C and 30°C |
|
||||
| duration | QuantityType<Time> | Duration of the override. Must be between 0 and 1440 minutes (24 hours) |
|
||||
|
||||
Example:
|
||||
|
||||
:::: tabs
|
||||
|
||||
::: tab DSL
|
||||
|
||||
```javascript
|
||||
getActions("warmup", "warmup:room:my_warmup:my_room").setOverride(18 | °C, 10 | min);
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
::: tab JavaScript
|
||||
|
||||
```javascript
|
||||
actions.get("warmup", "warmup:room:my_warmup:my_room").setOverride(Quantity("18 °C"), Quantity("10 min"));
|
||||
```
|
||||
:::
|
||||
|
||||
::::
|
||||
|
||||
## Full Example
|
||||
|
||||
### .things file
|
||||
|
@ -38,12 +38,15 @@ public class WarmupBindingConstants {
|
||||
// Room Channel Ids
|
||||
public static final String CHANNEL_CURRENT_TEMPERATURE = "currentTemperature";
|
||||
public static final String CHANNEL_TARGET_TEMPERATURE = "targetTemperature";
|
||||
public static final String CHANNEL_FIXED_TEMPERATURE = "fixedTemperature";
|
||||
public static final String CHANNEL_ENERGY = "energyToday";
|
||||
public static final String CHANNEL_OVERRIDE_DURATION = "overrideRemaining";
|
||||
public static final String CHANNEL_RUN_MODE = "runMode";
|
||||
public static final String CHANNEL_FROST_PROTECTION_MODE = "frostProtectionMode";
|
||||
public static final String CHANNEL_HEATING_TARGET = "heatingTarget";
|
||||
public static final String CHANNEL_AIR_TEMPERATURE = "airTemperature";
|
||||
public static final String CHANNEL_FLOOR_TEMPERATURE = "floorTemperature";
|
||||
public static final String CHANNEL_FLOOR1_TEMPERATURE = "floor1Temperature";
|
||||
public static final String CHANNEL_FLOOR2_TEMPERATURE = "floor2Temperature";
|
||||
|
||||
public static final String FROST_PROTECTION_MODE = "anti_frost";
|
||||
|
||||
@ -63,4 +66,8 @@ public class WarmupBindingConstants {
|
||||
|
||||
public static final String AUTH_METHOD = "userLogin";
|
||||
public static final String AUTH_APP_ID = "WARMUP-APP-V001";
|
||||
|
||||
public enum RoomMode {
|
||||
SCHEDULE
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,75 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.warmup.internal.action;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.warmup.internal.handler.RoomHandler;
|
||||
import org.openhab.core.automation.annotation.ActionInput;
|
||||
import org.openhab.core.automation.annotation.RuleAction;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.thing.binding.ThingActions;
|
||||
import org.openhab.core.thing.binding.ThingActionsScope;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @author James Melville - Initial contribution
|
||||
*/
|
||||
@ThingActionsScope(name = "warmup")
|
||||
@NonNullByDefault
|
||||
public class WarmupActions implements ThingActions {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(WarmupActions.class);
|
||||
|
||||
private @Nullable RoomHandler handler;
|
||||
|
||||
public WarmupActions() {
|
||||
logger.debug("Warmup action service instantiated");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setThingHandler(@Nullable ThingHandler handler) {
|
||||
if (handler instanceof RoomHandler roomHandler) {
|
||||
this.handler = roomHandler;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ThingHandler getThingHandler() {
|
||||
return handler;
|
||||
}
|
||||
|
||||
@RuleAction(label = "override", description = "Overrides the thermostat state for a specified time")
|
||||
public void setOverride(
|
||||
@ActionInput(name = "temperature", label = "Temperature") @Nullable QuantityType<?> temperature,
|
||||
@ActionInput(name = "duration", label = "Duration") @Nullable QuantityType<?> duration) {
|
||||
logger.debug("setOverride action called");
|
||||
RoomHandler handler = this.handler;
|
||||
if (handler != null && temperature != null && duration != null) {
|
||||
handler.setOverride(temperature, duration);
|
||||
} else {
|
||||
logger.warn("Warmup Action service argument is null!");
|
||||
}
|
||||
}
|
||||
|
||||
public static void setOverride(@Nullable ThingActions actions, @Nullable QuantityType<?> temperature,
|
||||
@Nullable QuantityType<?> duration) {
|
||||
if (actions instanceof WarmupActions warmupActions) {
|
||||
warmupActions.setOverride(temperature, duration);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Instance is not a WarmupActions class.");
|
||||
}
|
||||
}
|
||||
}
|
@ -87,8 +87,8 @@ public class MyWarmupApi {
|
||||
|
||||
AuthResponseDTO ar = GSON.fromJson(response.getContentAsString(), AuthResponseDTO.class);
|
||||
|
||||
if (ar != null && ar.getStatus() != null && "success".equals(ar.getStatus().getResult())) {
|
||||
authToken = ar.getResponse().getToken();
|
||||
if (ar != null && ar.status() != null && "success".equals(ar.status().result())) {
|
||||
authToken = ar.response().token();
|
||||
} else {
|
||||
throw new MyWarmupApiException("Authentication Failed");
|
||||
}
|
||||
@ -103,11 +103,39 @@ public class MyWarmupApi {
|
||||
public synchronized QueryResponseDTO getStatus() throws MyWarmupApiException {
|
||||
return callWarmupGraphQL("""
|
||||
query QUERY { user { locations{ id name \
|
||||
rooms { id roomName runMode overrideDur targetTemp currentTemp \
|
||||
thermostat4ies{ deviceSN lastPoll }}}}}\
|
||||
rooms { id roomName energy runMode overrideDur targetTemp currentTemp fixedTemp \
|
||||
thermostat4ies{ deviceSN lastPoll airTemp floor1Temp floor2Temp }}}}}\
|
||||
""");
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the API to set the room mode to program
|
||||
*
|
||||
* @param locationId Id of the location
|
||||
* @param roomId Id of the room
|
||||
* @param mode RoomMode defined in enum
|
||||
* @throws MyWarmupApiException API callout error
|
||||
*/
|
||||
public void setRoomMode(String locationId, String roomId, WarmupBindingConstants.RoomMode mode)
|
||||
throws MyWarmupApiException {
|
||||
if (WarmupBindingConstants.RoomMode.SCHEDULE.equals(mode)) {
|
||||
callWarmupGraphQL("mutation{deviceProgram(lid:%s,rid:%s)}".formatted(locationId, roomId));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the API to set the room mode to fixed with a specific temperature
|
||||
*
|
||||
* @param locationId Id of the location
|
||||
* @param roomId Id of the room
|
||||
* @param temperature Temperature to set * 10
|
||||
* @throws MyWarmupApiException API callout error
|
||||
*/
|
||||
public void setFixed(String locationId, String roomId, int temperature) throws MyWarmupApiException {
|
||||
callWarmupGraphQL(
|
||||
"mutation{deviceFixed(lid:%s,rid:%s,temperature:%d)}".formatted(locationId, roomId, temperature));
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the API to set a temperature override on a specific room
|
||||
*
|
||||
@ -119,7 +147,7 @@ public class MyWarmupApi {
|
||||
*/
|
||||
public void setOverride(String locationId, String roomId, int temperature, Integer duration)
|
||||
throws MyWarmupApiException {
|
||||
callWarmupGraphQL(String.format("mutation{deviceOverride(lid:%s,rid:%s,temperature:%d,minutes:%d)}", locationId,
|
||||
callWarmupGraphQL("mutation{deviceOverride(lid:%s,rid:%s,temperature:%d,minutes:%d)}".formatted(locationId,
|
||||
roomId, temperature, duration));
|
||||
}
|
||||
|
||||
@ -133,7 +161,7 @@ public class MyWarmupApi {
|
||||
*/
|
||||
public void toggleFrostProtectionMode(String locationId, String roomId, OnOffType command)
|
||||
throws MyWarmupApiException {
|
||||
callWarmupGraphQL(String.format("mutation{turn%s(lid:%s,rid:%s){id}}", command == OnOffType.ON ? "Off" : "On",
|
||||
callWarmupGraphQL("mutation{turn%s(lid:%s,rid:%s){id}}".formatted(command == OnOffType.ON ? "Off" : "On",
|
||||
locationId, roomId));
|
||||
}
|
||||
|
||||
@ -144,7 +172,7 @@ public class MyWarmupApi {
|
||||
|
||||
QueryResponseDTO qr = GSON.fromJson(response.getContentAsString(), QueryResponseDTO.class);
|
||||
|
||||
if (qr != null && "success".equals(qr.getStatus())) {
|
||||
if (qr != null && "success".equals(qr.status())) {
|
||||
return qr;
|
||||
} else {
|
||||
throw new MyWarmupApiException("Unexpected reponse from API");
|
||||
|
@ -67,8 +67,8 @@ public class WarmupDiscoveryService extends AbstractThingHandlerDiscoveryService
|
||||
public void refresh(@Nullable QueryResponseDTO domain) {
|
||||
if (domain != null) {
|
||||
HashSet<ThingUID> discoveredThings = new HashSet<>();
|
||||
for (LocationDTO location : domain.getData().getUser().getLocations()) {
|
||||
for (RoomDTO room : location.getRooms()) {
|
||||
for (LocationDTO location : domain.data().user().locations()) {
|
||||
for (RoomDTO room : location.rooms()) {
|
||||
discoverRoom(location, room, discoveredThings);
|
||||
}
|
||||
}
|
||||
@ -76,20 +76,20 @@ public class WarmupDiscoveryService extends AbstractThingHandlerDiscoveryService
|
||||
}
|
||||
|
||||
private void discoverRoom(LocationDTO location, RoomDTO room, HashSet<ThingUID> discoveredThings) {
|
||||
if (room.getThermostat4ies() != null && !room.getThermostat4ies().isEmpty()) {
|
||||
final String deviceSN = room.getThermostat4ies().get(0).getDeviceSN();
|
||||
if (room.thermostat4ies() != null && !room.thermostat4ies().isEmpty()) {
|
||||
final String deviceSN = room.thermostat4ies().get(0).deviceSN();
|
||||
ThingUID localBridgeUID = this.bridgeUID;
|
||||
if (localBridgeUID != null && deviceSN != null) {
|
||||
final Map<String, Object> roomProperties = new HashMap<>();
|
||||
roomProperties.put(Thing.PROPERTY_SERIAL_NUMBER, deviceSN);
|
||||
roomProperties.put(PROPERTY_ROOM_ID, room.getId());
|
||||
roomProperties.put(PROPERTY_ROOM_NAME, room.getName());
|
||||
roomProperties.put(PROPERTY_ROOM_NAME, room.roomName());
|
||||
roomProperties.put(PROPERTY_LOCATION_ID, location.getId());
|
||||
roomProperties.put(PROPERTY_LOCATION_NAME, location.getName());
|
||||
roomProperties.put(PROPERTY_LOCATION_NAME, location.name());
|
||||
|
||||
ThingUID roomThingUID = new ThingUID(THING_TYPE_ROOM, localBridgeUID, deviceSN);
|
||||
thingDiscovered(DiscoveryResultBuilder.create(roomThingUID).withBridge(localBridgeUID)
|
||||
.withProperties(roomProperties).withLabel(location.getName() + " - " + room.getName())
|
||||
.withProperties(roomProperties).withLabel(location.name() + " - " + room.roomName())
|
||||
.withRepresentationProperty(Thing.PROPERTY_SERIAL_NUMBER).build());
|
||||
|
||||
discoveredThings.add(roomThingUID);
|
||||
|
@ -14,21 +14,27 @@ package org.openhab.binding.warmup.internal.handler;
|
||||
|
||||
import static org.openhab.binding.warmup.internal.WarmupBindingConstants.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.warmup.internal.action.WarmupActions;
|
||||
import org.openhab.binding.warmup.internal.api.MyWarmupApi;
|
||||
import org.openhab.binding.warmup.internal.api.MyWarmupApiException;
|
||||
import org.openhab.binding.warmup.internal.model.query.LocationDTO;
|
||||
import org.openhab.binding.warmup.internal.model.query.QueryResponseDTO;
|
||||
import org.openhab.binding.warmup.internal.model.query.RoomDTO;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.unit.SIUnits;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.library.unit.Units;
|
||||
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.ThingHandlerService;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -64,9 +70,16 @@ public class RoomHandler extends WarmupThingHandler implements WarmupRefreshList
|
||||
&& command instanceof QuantityType<?> quantityCommand) {
|
||||
setOverride(quantityCommand);
|
||||
}
|
||||
if (CHANNEL_FIXED_TEMPERATURE.equals(channelUID.getId())
|
||||
&& command instanceof QuantityType<?> quantityCommand) {
|
||||
setFixed(quantityCommand);
|
||||
}
|
||||
if (CHANNEL_FROST_PROTECTION_MODE.equals(channelUID.getId()) && command instanceof OnOffType onOffCommand) {
|
||||
toggleFrostProtectionMode(onOffCommand);
|
||||
}
|
||||
if (CHANNEL_RUN_MODE.equals(channelUID.getId()) && command instanceof StringType stringCommand) {
|
||||
setRoomMode(stringCommand);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -80,27 +93,35 @@ public class RoomHandler extends WarmupThingHandler implements WarmupRefreshList
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "No data from bridge");
|
||||
} else if (config != null) {
|
||||
final String serialNumber = config.getSerialNumber();
|
||||
for (LocationDTO location : domain.getData().getUser().getLocations()) {
|
||||
for (RoomDTO room : location.getRooms()) {
|
||||
if (room.getThermostat4ies() != null && !room.getThermostat4ies().isEmpty()
|
||||
&& room.getThermostat4ies().get(0).getDeviceSN().equals(serialNumber)) {
|
||||
if (room.getThermostat4ies().get(0).getLastPoll() > 10) {
|
||||
for (LocationDTO location : domain.data().user().locations()) {
|
||||
for (RoomDTO room : location.rooms()) {
|
||||
if (room.thermostat4ies() != null && !room.thermostat4ies().isEmpty()
|
||||
&& room.thermostat4ies().get(0).deviceSN().equals(serialNumber)) {
|
||||
if (room.thermostat4ies().get(0).lastPoll() > 10) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"Thermostat has not polled for 10 minutes");
|
||||
} else {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
|
||||
updateProperty(PROPERTY_ROOM_ID, room.getId());
|
||||
updateProperty(PROPERTY_ROOM_NAME, room.getName());
|
||||
updateProperty(PROPERTY_ROOM_NAME, room.roomName());
|
||||
updateProperty(PROPERTY_LOCATION_ID, location.getId());
|
||||
updateProperty(PROPERTY_LOCATION_NAME, location.getName());
|
||||
updateProperty(PROPERTY_LOCATION_NAME, location.name());
|
||||
|
||||
updateState(CHANNEL_CURRENT_TEMPERATURE, parseTemperature(room.getCurrentTemperature()));
|
||||
updateState(CHANNEL_TARGET_TEMPERATURE, parseTemperature(room.getTargetTemperature()));
|
||||
updateState(CHANNEL_OVERRIDE_DURATION, parseDuration(room.getOverrideDuration()));
|
||||
updateState(CHANNEL_RUN_MODE, parseString(room.getRunMode()));
|
||||
updateState(CHANNEL_CURRENT_TEMPERATURE, parseTemperature(room.currentTemp()));
|
||||
updateState(CHANNEL_TARGET_TEMPERATURE, parseTemperature(room.targetTemp()));
|
||||
updateState(CHANNEL_FIXED_TEMPERATURE, parseTemperature(room.fixedTemp()));
|
||||
updateState(CHANNEL_ENERGY, parseEnergy(room.energy()));
|
||||
updateState(CHANNEL_AIR_TEMPERATURE,
|
||||
parseTemperature(room.thermostat4ies().get(0).airTemp()));
|
||||
updateState(CHANNEL_FLOOR1_TEMPERATURE,
|
||||
parseTemperature(room.thermostat4ies().get(0).floor1Temp()));
|
||||
updateState(CHANNEL_FLOOR2_TEMPERATURE,
|
||||
parseTemperature(room.thermostat4ies().get(0).floor2Temp()));
|
||||
updateState(CHANNEL_OVERRIDE_DURATION, parseDuration(room.overrideDur()));
|
||||
updateState(CHANNEL_RUN_MODE, parseString(room.runMode()));
|
||||
updateState(CHANNEL_FROST_PROTECTION_MODE,
|
||||
OnOffType.from(room.getRunMode().equals(FROST_PROTECTION_MODE)));
|
||||
OnOffType.from(room.runMode().equals(FROST_PROTECTION_MODE)));
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -112,41 +133,82 @@ public class RoomHandler extends WarmupThingHandler implements WarmupRefreshList
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Class<? extends ThingHandlerService>> getServices() {
|
||||
return Set.of(WarmupActions.class);
|
||||
}
|
||||
|
||||
private void setOverride(final QuantityType<?> command) {
|
||||
String roomId = getThing().getProperties().get(PROPERTY_ROOM_ID);
|
||||
String locationId = getThing().getProperties().get(PROPERTY_LOCATION_ID);
|
||||
setOverride(command, new QuantityType<>(config.getOverrideDuration(), Units.MINUTE));
|
||||
}
|
||||
|
||||
QuantityType<?> temp = command.toUnit(SIUnits.CELSIUS);
|
||||
|
||||
if (temp != null) {
|
||||
final int value = temp.multiply(BigDecimal.TEN).intValue();
|
||||
public void setOverride(final QuantityType<?> temperature, final QuantityType<?> duration) {
|
||||
setOverride(formatTemperature(temperature), duration.toUnit(Units.MINUTE).intValue());
|
||||
}
|
||||
|
||||
private void setOverride(final int temperature, final int duration) {
|
||||
if (duration > 1440 || duration <= 0) {
|
||||
logger.warn("Set Override failed: duration must be between 0 and 1440 minutes");
|
||||
}
|
||||
if (temperature > 600 || temperature < 50) {
|
||||
logger.warn("Set Override failed: temperature must be between 0.5 and 60 degrees C");
|
||||
} else {
|
||||
try {
|
||||
final MyWarmupAccountHandler bridgeHandler = getBridgeHandler();
|
||||
if (bridgeHandler != null && config != null) {
|
||||
final int overrideDuration = config.getOverrideDuration();
|
||||
if (overrideDuration > 0 && locationId != null && roomId != null) {
|
||||
bridgeHandler.getApi().setOverride(locationId, roomId, value, overrideDuration);
|
||||
refreshFromServer();
|
||||
}
|
||||
}
|
||||
RoomCallout rc = getCallout();
|
||||
rc.api.setOverride(rc.locationId, rc.roomId, temperature, duration);
|
||||
refreshFromServer();
|
||||
} catch (MyWarmupApiException e) {
|
||||
logger.debug("Set Override failed: {}", e.getMessage());
|
||||
logger.warn("Set Override failed: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setFixed(final QuantityType<?> command) {
|
||||
try {
|
||||
RoomCallout rc = getCallout();
|
||||
rc.api.setFixed(rc.locationId, rc.roomId, formatTemperature(command));
|
||||
refreshFromServer();
|
||||
} catch (MyWarmupApiException e) {
|
||||
logger.warn("Set Fixed failed: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void toggleFrostProtectionMode(OnOffType command) {
|
||||
String roomId = getThing().getProperties().get(PROPERTY_ROOM_ID);
|
||||
String locationId = getThing().getProperties().get(PROPERTY_LOCATION_ID);
|
||||
try {
|
||||
final MyWarmupAccountHandler bridgeHandler = getBridgeHandler();
|
||||
if (bridgeHandler != null && locationId != null && roomId != null) {
|
||||
bridgeHandler.getApi().toggleFrostProtectionMode(locationId, roomId, command);
|
||||
refreshFromServer();
|
||||
}
|
||||
RoomCallout rc = getCallout();
|
||||
rc.api.toggleFrostProtectionMode(rc.locationId, rc.roomId, command);
|
||||
refreshFromServer();
|
||||
} catch (MyWarmupApiException e) {
|
||||
logger.debug("Toggle Frost Protection failed: {}", e.getMessage());
|
||||
logger.warn("Toggle Frost Protection failed: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void setRoomMode(StringType command) {
|
||||
try {
|
||||
RoomCallout rc = getCallout();
|
||||
RoomMode mode = RoomMode.valueOf(command.toString().trim().toUpperCase());
|
||||
rc.api.setRoomMode(rc.locationId, rc.roomId, mode);
|
||||
refreshFromServer();
|
||||
} catch (MyWarmupApiException e) {
|
||||
logger.warn("Set Room Mode failed: {}", e.getMessage());
|
||||
} catch (IllegalArgumentException ex) {
|
||||
logger.warn("Unable to set room mode: {}", command.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private RoomCallout getCallout() throws MyWarmupApiException {
|
||||
Map<String, String> props = getThing().getProperties();
|
||||
String locationId = props.get(PROPERTY_LOCATION_ID);
|
||||
String roomId = props.get(PROPERTY_ROOM_ID);
|
||||
final MyWarmupAccountHandler bridgeHandler = getBridgeHandler();
|
||||
|
||||
if (bridgeHandler != null && locationId != null && roomId != null) {
|
||||
return new RoomCallout(roomId, locationId, bridgeHandler.getApi());
|
||||
} else {
|
||||
throw new MyWarmupApiException("Misconfigured thing.");
|
||||
}
|
||||
}
|
||||
|
||||
record RoomCallout(String roomId, String locationId, MyWarmupApi api) {
|
||||
}
|
||||
}
|
||||
|
@ -77,6 +77,19 @@ public class WarmupThingHandler extends BaseThingHandler {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param temperature value returned from the API as a String * 10. i.e. "215" = 21.5 degrees C
|
||||
* @return the temperature as a {@link QuantityType}
|
||||
*/
|
||||
protected State parseTemperature(@Nullable String temperature) {
|
||||
try {
|
||||
return temperature != null ? parseTemperature(Integer.parseInt(temperature)) : UnDefType.UNDEF;
|
||||
} catch (NumberFormatException e) {
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param temperature value returned from the API as an Integer * 10. i.e. 215 = 21.5 degrees C
|
||||
@ -86,6 +99,28 @@ public class WarmupThingHandler extends BaseThingHandler {
|
||||
return temperature != null ? new QuantityType<>(temperature / 10.0, SIUnits.CELSIUS) : UnDefType.UNDEF;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param temperature {@link QuantityType} a temperature
|
||||
* @return the temperature as an int in degrees C * 10. i.e. 21.5 degrees C = 215
|
||||
*/
|
||||
protected int formatTemperature(QuantityType<?> temperature) {
|
||||
return (int) (temperature.toUnit(SIUnits.CELSIUS).doubleValue() * 10);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param enery value returned from the API as a string "10.5" = 10.5 kWh
|
||||
* @return the energy as a {@link QuantityType}
|
||||
*/
|
||||
protected State parseEnergy(@Nullable String energy) {
|
||||
try {
|
||||
return energy != null ? new QuantityType<>(Float.parseFloat(energy), Units.KILOWATT_HOUR) : UnDefType.UNDEF;
|
||||
} catch (NumberFormatException e) {
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param value a string to convert to {@link StringType}
|
||||
|
@ -15,16 +15,9 @@ package org.openhab.binding.warmup.internal.model.auth;
|
||||
/**
|
||||
* @author James Melville - Initial contribution
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class AuthRequestDTO {
|
||||
|
||||
private AuthRequestDataDTO request;
|
||||
public record AuthRequestDTO(AuthRequestDataDTO request) {
|
||||
|
||||
public AuthRequestDTO(String email, String password, String method, String appId) {
|
||||
setRequest(new AuthRequestDataDTO(email, password, method, appId));
|
||||
}
|
||||
|
||||
public void setRequest(AuthRequestDataDTO request) {
|
||||
this.request = request;
|
||||
this(new AuthRequestDataDTO(email, password, method, appId));
|
||||
}
|
||||
}
|
||||
|
@ -15,33 +15,5 @@ package org.openhab.binding.warmup.internal.model.auth;
|
||||
/**
|
||||
* @author James Melville - Initial contribution
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class AuthRequestDataDTO {
|
||||
private String email;
|
||||
private String password;
|
||||
private String method;
|
||||
private String appId;
|
||||
|
||||
public AuthRequestDataDTO(String email, String password, String method, String appId) {
|
||||
this.setEmail(email);
|
||||
this.setPassword(password);
|
||||
this.setMethod(method);
|
||||
this.setAppId(appId);
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public void setMethod(String method) {
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
public void setAppId(String appId) {
|
||||
this.appId = appId;
|
||||
}
|
||||
public record AuthRequestDataDTO(String email, String password, String method, String appId) {
|
||||
}
|
||||
|
@ -15,16 +15,5 @@ package org.openhab.binding.warmup.internal.model.auth;
|
||||
/**
|
||||
* @author James Melville - Initial contribution
|
||||
*/
|
||||
public class AuthResponseDTO {
|
||||
|
||||
private AuthResponseStatusDTO status;
|
||||
private AuthResponseDataDTO response;
|
||||
|
||||
public AuthResponseStatusDTO getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public AuthResponseDataDTO getResponse() {
|
||||
return response;
|
||||
}
|
||||
public record AuthResponseDTO(AuthResponseStatusDTO status, AuthResponseDataDTO response) {
|
||||
}
|
||||
|
@ -15,15 +15,5 @@ package org.openhab.binding.warmup.internal.model.auth;
|
||||
/**
|
||||
* @author James Melville - Initial contribution
|
||||
*/
|
||||
public class AuthResponseDataDTO {
|
||||
private String method;
|
||||
private String token;
|
||||
|
||||
public String getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
public String getMethod() {
|
||||
return method;
|
||||
}
|
||||
public record AuthResponseDataDTO(String method, String token) {
|
||||
}
|
||||
|
@ -15,10 +15,5 @@ package org.openhab.binding.warmup.internal.model.auth;
|
||||
/**
|
||||
* @author James Melville - Initial contribution
|
||||
*/
|
||||
public class AuthResponseStatusDTO {
|
||||
private String result;
|
||||
|
||||
public String getResult() {
|
||||
return result;
|
||||
}
|
||||
public record AuthResponseStatusDTO(String result) {
|
||||
}
|
||||
|
@ -15,16 +15,5 @@ package org.openhab.binding.warmup.internal.model.query;
|
||||
/**
|
||||
* @author James Melville - Initial contribution
|
||||
*/
|
||||
public class DeviceDTO {
|
||||
|
||||
private String deviceSN;
|
||||
private int lastPoll;
|
||||
|
||||
public String getDeviceSN() {
|
||||
return deviceSN;
|
||||
}
|
||||
|
||||
public int getLastPoll() {
|
||||
return lastPoll;
|
||||
}
|
||||
public record DeviceDTO(String deviceSN, String airTemp, String floor1Temp, String floor2Temp, int lastPoll) {
|
||||
}
|
||||
|
@ -17,21 +17,11 @@ import java.util.List;
|
||||
/**
|
||||
* @author James Melville - Initial contribution
|
||||
*/
|
||||
public class LocationDTO {
|
||||
public record LocationDTO
|
||||
|
||||
private int id;
|
||||
private String name;
|
||||
private List<RoomDTO> rooms;
|
||||
(int id, String name, List<RoomDTO> rooms) {
|
||||
|
||||
public String getId() {
|
||||
return String.valueOf(id);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public List<RoomDTO> getRooms() {
|
||||
return rooms;
|
||||
}
|
||||
}
|
||||
|
@ -15,11 +15,5 @@ package org.openhab.binding.warmup.internal.model.query;
|
||||
/**
|
||||
* @author James Melville - Initial contribution
|
||||
*/
|
||||
public class QueryDataDTO {
|
||||
|
||||
private UserDTO user;
|
||||
|
||||
public UserDTO getUser() {
|
||||
return user;
|
||||
}
|
||||
public record QueryDataDTO(UserDTO user) {
|
||||
}
|
||||
|
@ -15,16 +15,7 @@ package org.openhab.binding.warmup.internal.model.query;
|
||||
/**
|
||||
* @author James Melville - Initial contribution
|
||||
*/
|
||||
public class QueryResponseDTO {
|
||||
public record QueryResponseDTO
|
||||
|
||||
private QueryDataDTO data;
|
||||
private String status;
|
||||
|
||||
public QueryDataDTO getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
(QueryDataDTO data, String status) {
|
||||
}
|
||||
|
@ -17,41 +17,12 @@ import java.util.List;
|
||||
/**
|
||||
* @author James Melville - Initial contribution
|
||||
*/
|
||||
public class RoomDTO {
|
||||
public record RoomDTO(
|
||||
|
||||
private int id;
|
||||
private String roomName;
|
||||
private Integer currentTemp;
|
||||
private Integer targetTemp;
|
||||
private String runMode;
|
||||
private Integer overrideDur;
|
||||
private List<DeviceDTO> thermostat4ies;
|
||||
int id, String roomName, Integer currentTemp, Integer targetTemp, Integer fixedTemp, String energy,
|
||||
String runMode, Integer overrideDur, List<DeviceDTO> thermostat4ies) {
|
||||
|
||||
public String getId() {
|
||||
return String.valueOf(id);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return roomName;
|
||||
}
|
||||
|
||||
public Integer getCurrentTemperature() {
|
||||
return currentTemp;
|
||||
}
|
||||
|
||||
public Integer getTargetTemperature() {
|
||||
return targetTemp;
|
||||
}
|
||||
|
||||
public String getRunMode() {
|
||||
return runMode;
|
||||
}
|
||||
|
||||
public Integer getOverrideDuration() {
|
||||
return overrideDur;
|
||||
}
|
||||
|
||||
public List<DeviceDTO> getThermostat4ies() {
|
||||
return thermostat4ies;
|
||||
}
|
||||
}
|
||||
|
@ -17,11 +17,5 @@ import java.util.List;
|
||||
/**
|
||||
* @author James Melville - Initial contribution
|
||||
*/
|
||||
public class UserDTO {
|
||||
|
||||
private List<LocationDTO> locations;
|
||||
|
||||
public List<LocationDTO> getLocations() {
|
||||
return locations;
|
||||
}
|
||||
public record UserDTO(List<LocationDTO> locations) {
|
||||
}
|
||||
|
@ -5,7 +5,8 @@
|
||||
|
||||
<type>binding</type>
|
||||
<name>Warmup Binding</name>
|
||||
<description>This is the binding for a Warmup 4iE Thermostat primarily used for controlling underfloor heating.</description>
|
||||
<description>This is the binding for Warmup WiFi connected Thermostats primarily used for controlling underfloor
|
||||
heating.</description>
|
||||
<connection>cloud</connection>
|
||||
|
||||
</addon:addon>
|
||||
|
@ -1,14 +1,14 @@
|
||||
# add-on
|
||||
|
||||
addon.warmup.name = Warmup Binding
|
||||
addon.warmup.description = This is the binding for a Warmup 4iE Thermostat primarily used for controlling underfloor heating.
|
||||
addon.warmup.description = This is the binding for Warmup WiFi connected Thermostats primarily used for controlling underfloor heating.
|
||||
|
||||
# thing types
|
||||
|
||||
thing-type.warmup.my-warmup.label = My Warmup Account
|
||||
thing-type.warmup.my-warmup.description = Connection to the https://my.warmup.com site
|
||||
thing-type.warmup.room.label = Room
|
||||
thing-type.warmup.room.description = Warmup 4iE Device controlling a room
|
||||
thing-type.warmup.room.description = Warmup WiFi connected Thermostat(s) controlling a room
|
||||
|
||||
# thing types config
|
||||
|
||||
@ -24,8 +24,18 @@ thing-type.config.warmup.room.serialNumber.label = Serial Number
|
||||
|
||||
# channel types
|
||||
|
||||
channel-type.warmup.airTemperature.label = Air Temperature
|
||||
channel-type.warmup.airTemperature.description = Currently reported air temperature at the device
|
||||
channel-type.warmup.currentTemperature.label = Current Temperature
|
||||
channel-type.warmup.currentTemperature.description = Current temperature in room, may be air or floor dependent on Heating Target
|
||||
channel-type.warmup.energyToday.label = Energy Today
|
||||
channel-type.warmup.energyToday.label = Today's current energy consumption.
|
||||
channel-type.warmup.fixedTemperature.label = Fixed Temperature
|
||||
channel-type.warmup.fixedTemperature.description = Target temperature for fixed mode on device
|
||||
channel-type.warmup.floor1Temperature.label = Floor 1 Temperature
|
||||
channel-type.warmup.floor1Temperature.description = Currently reported temperature from floor probe 1 on the device
|
||||
channel-type.warmup.floor2Temperature.label = Floor 2 Temperature
|
||||
channel-type.warmup.floor2Temperature.description = Currently reported temperature from floor probe 2 on the device
|
||||
channel-type.warmup.frostProtectionMode.label = Frost Protection Mode
|
||||
channel-type.warmup.overrideRemaining.label = Override Remaining
|
||||
channel-type.warmup.overrideRemaining.description = How long until the override deactivates
|
||||
|
@ -33,17 +33,26 @@
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Room</label>
|
||||
<description>Warmup 4iE Device controlling a room</description>
|
||||
<description>Warmup WiFi connected Thermostat(s) controlling a room</description>
|
||||
<category>RadiatorControl</category>
|
||||
|
||||
<channels>
|
||||
<channel id="currentTemperature" typeId="currentTemperature"/>
|
||||
<channel id="currentTemperature" typeId="system.indoor-temperature"/>
|
||||
<channel id="targetTemperature" typeId="targetTemperature"/>
|
||||
<channel id="fixedTemperature" typeId="fixedTemperature"/>
|
||||
<channel id="overrideRemaining" typeId="overrideRemaining"/>
|
||||
<channel id="energyToday" typeId="system.electric-energy"/>
|
||||
<channel id="runMode" typeId="runMode"/>
|
||||
<channel id="frostProtectionMode" typeId="frostProtectionMode"/>
|
||||
<channel id="airTemperature" typeId="system.indoor-temperature"/>
|
||||
<channel id="floor1Temperature" typeId="system.indoor-temperature"/>
|
||||
<channel id="floor2Temperature" typeId="system.indoor-temperature"/>
|
||||
</channels>
|
||||
|
||||
<properties>
|
||||
<property name="thingTypeVersion">1</property>
|
||||
</properties>
|
||||
|
||||
<representation-property>serialNumber</representation-property>
|
||||
|
||||
<config-description>
|
||||
@ -58,14 +67,6 @@
|
||||
</config-description>
|
||||
</thing-type>
|
||||
|
||||
<channel-type id="currentTemperature">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Current Temperature</label>
|
||||
<description>Current temperature in room, may be air or floor dependent on Heating Target</description>
|
||||
<category>Temperature</category>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="targetTemperature">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Target Temperature</label>
|
||||
@ -74,6 +75,14 @@
|
||||
<state min="5" max="30" step="0.5" readOnly="false" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="fixedTemperature">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Fixed Temperature</label>
|
||||
<description>Target temperature for fixed mode on device</description>
|
||||
<category>Heating</category>
|
||||
<state min="5" max="30" step="0.5" readOnly="false" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="overrideRemaining">
|
||||
<item-type>Number:Time</item-type>
|
||||
<label>Override Remaining</label>
|
||||
@ -86,7 +95,7 @@
|
||||
<item-type>String</item-type>
|
||||
<label>Run Mode</label>
|
||||
<description>The heat regulation mode of the thermostat</description>
|
||||
<state readOnly="true">
|
||||
<state>
|
||||
<options>
|
||||
<option value="not_set">Not Set</option>
|
||||
<option value="off">Off</option>
|
||||
|
@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||
<update:update-descriptions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:update="https://openhab.org/schemas/update-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/update-description/v1.0.0 https://openhab.org/schemas/update-description-1.0.0.xsd">
|
||||
|
||||
<thing-type uid="warmup:room">
|
||||
<instruction-set targetVersion="1">
|
||||
<add-channel id="fixedTemperature">
|
||||
<type>warmup:fixedTemperature</type>
|
||||
<label>Fixed Temperature</label>
|
||||
</add-channel>
|
||||
<add-channel id="energyToday">
|
||||
<type>system.electric-energy</type>
|
||||
<label>Energy Today</label>
|
||||
</add-channel>
|
||||
<add-channel id="airTemperature">
|
||||
<type>system.indoor-temperature</type>
|
||||
<label>Air Temperature</label>
|
||||
</add-channel>
|
||||
<add-channel id="floor1Temperature">
|
||||
<type>system.indoor-temperature</type>
|
||||
<label>Floor 1 Temperature</label>
|
||||
</add-channel>
|
||||
<add-channel id="floor2Temperature">
|
||||
<type>system.indoor-temperature</type>
|
||||
<label>Floor 2 Temperature</label>
|
||||
</add-channel>
|
||||
<update-channel id="currentTemperature">
|
||||
<type>system.indoor-temperature</type>
|
||||
</update-channel>
|
||||
</instruction-set>
|
||||
</thing-type>
|
||||
</update:update-descriptions>
|
Loading…
Reference in New Issue
Block a user