[loxone] Sauna controller implementation (#11270)

Signed-off-by: Pawel Pieczul <pieczul@gmail.com>
This commit is contained in:
Pawel Pieczul 2021-11-18 00:40:26 +01:00 committed by GitHub
parent 0165ea7659
commit 5ffac34c7a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 743 additions and 4 deletions

View File

@ -30,6 +30,7 @@ import org.openhab.binding.loxone.internal.types.LxState;
import org.openhab.binding.loxone.internal.types.LxUuid;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID;
@ -130,6 +131,8 @@ public class LxControl {
Map<String, String> outputs;
Boolean presenceConnected;
Integer connectedInputs;
Boolean hasVaporizer;
Boolean hasDoorSensor;
}
/**
@ -586,6 +589,24 @@ public class LxControl {
return null;
}
/**
* Gets value of a state object of given name, if exists, and converts it to percent type value.
* Assumes the state value is between 0.0-100.0 which corresponds directly to 0-100 percent.
*
* @param name state name
* @return state value
*/
State getStatePercentValue(String name) {
Double value = getStateDoubleValue(name);
if (value == null) {
return null;
}
if (value >= 0.0 && value <= 100.0) {
return new PercentType(value.intValue());
}
return UnDefType.UNDEF;
}
/**
* Gets text value of a state object of given name, if exists
*

View File

@ -43,6 +43,7 @@ class LxControlFactory {
add(new LxControlMeter.Factory());
add(new LxControlPushbutton.Factory());
add(new LxControlRadio.Factory());
add(new LxControlSauna.Factory());
add(new LxControlSlider.Factory());
add(new LxControlSwitch.Factory());
add(new LxControlTextState.Factory());

View File

@ -0,0 +1,191 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.loxone.internal.controls;
import static org.openhab.binding.loxone.internal.LxBindingConstants.*;
import java.io.IOException;
import org.openhab.binding.loxone.internal.types.LxUuid;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.type.ChannelTypeUID;
import org.openhab.core.types.Command;
import org.openhab.core.types.State;
/**
* Loxone Miniserver's Sauna
*
* @author Pawel Pieczul - initial contribution
*
*/
class LxControlSauna extends LxControl {
static class Factory extends LxControlInstance {
@Override
LxControl create(LxUuid uuid) {
return new LxControlSauna(uuid);
}
@Override
String getType() {
return "sauna";
}
}
private static final String STATE_ACTIVE = "active";
private static final String STATE_POWER_LEVEL = "power";
private static final String STATE_TEMP_ACTUAL = "tempactual";
private static final String STATE_TEMP_BENCH = "tempbench";
private static final String STATE_TEMP_TARGET = "temptarget";
private static final String STATE_FAN = "fan";
private static final String STATE_DRYING = "drying";
private static final String STATE_DOOR_CLOSED = "doorclosed";
private static final String STATE_ERROR = "error";
private static final String STATE_VAPOR_POWER_LEVEL = "vaporpower";
private static final String STATE_SAUNA_ERROR = "saunaerror";
private static final String STATE_TIMER = "timer";
private static final String STATE_TIMER_TOTAL = "timertotal";
private static final String STATE_OUT_OF_WATER = "lesswater";
private static final String STATE_HUMIDITY_ACTUAL = "humidityactual";
private static final String STATE_HUMIDITY_TARGET = "humiditytarget";
private static final String STATE_EVAPORATOR_MODE = "mode";
private static final String CMD_ON = "on";
private static final String CMD_OFF = "off";
private static final String CMD_FAN_ON = "fanon";
private static final String CMD_FAN_OFF = "fanoff";
private static final String CMD_SET_TEMP_TARGET = "temp/";
private static final String CMD_SET_HUMIDITY_TARGET = "humidity/";
private static final String CMD_SET_EVAPORATOR_MODE = "mode/";
private static final String CMD_NEXT_STATE = "pulse";
private static final String CMD_START_TIMER = "starttimer";
LxControlSauna(LxUuid uuid) {
super(uuid);
}
@Override
public void initialize(LxControlConfig config) {
super.initialize(config);
addChannel("Switch", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_SWITCH),
defaultChannelLabel + " / Active", "Sauna Active", tags, this::handleSaunaActivateCommands,
() -> getStateOnOffValue(STATE_ACTIVE));
addChannel("Number", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_RO_NUMBER),
defaultChannelLabel + " / Power", "Sauna Power Level", tags, null,
() -> getStatePercentValue(STATE_POWER_LEVEL));
addChannel("Number", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_RO_NUMBER),
defaultChannelLabel + " / Temperature / Actual", "Actual Temperature", tags, null,
() -> getStateDecimalValue(STATE_TEMP_ACTUAL));
addChannel("Number", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_RO_NUMBER),
defaultChannelLabel + " / Temperature / Bench", "Bench Temperature", tags, null,
() -> getStateDecimalValue(STATE_TEMP_BENCH));
addChannel("Number", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_NUMBER),
defaultChannelLabel + " / Temperature / Target", "Target Temperature", tags,
(cmd) -> handleSetNumberCommands(cmd, CMD_SET_TEMP_TARGET),
() -> getStateDecimalValue(STATE_TEMP_TARGET));
addChannel("Switch", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_SWITCH),
defaultChannelLabel + " / Fan", "Fan", tags, this::handleFanCommands,
() -> getStateOnOffValue(STATE_FAN));
addChannel("Switch", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_RO_SWITCH),
defaultChannelLabel + " / Drying", "Drying", tags, null, () -> getStateOnOffValue(STATE_DRYING));
if (details != null && details.hasDoorSensor != null && details.hasDoorSensor) {
addChannel("Switch", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_RO_SWITCH),
defaultChannelLabel + " / Door Closed", "Door Closed", tags, null,
() -> getStateOnOffValue(STATE_DOOR_CLOSED));
}
addChannel("Number", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_RO_NUMBER),
defaultChannelLabel + " / Error Code", "Error Code", tags, null, () -> getStateErrorValue());
addChannel("Number", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_RO_NUMBER),
defaultChannelLabel + " / Timer / Current", "Current Timer Value", tags, null,
() -> getStateDecimalValue(STATE_TIMER));
addChannel("Switch", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_SWITCH),
defaultChannelLabel + " / Timer / Trigger", "Start Timer", tags,
(cmd) -> handleTriggerCommands(cmd, CMD_START_TIMER), () -> OnOffType.OFF);
addChannel("Number", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_RO_NUMBER),
defaultChannelLabel + " / Timer / Total", "Total Timer Value", tags, null,
() -> getStateDecimalValue(STATE_TIMER_TOTAL));
if (details != null && details.hasVaporizer != null && details.hasVaporizer) {
addChannel("Number", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_RO_NUMBER),
defaultChannelLabel + " / Evaporator / Power", "Evaporator Power Level", tags, null,
() -> getStatePercentValue(STATE_VAPOR_POWER_LEVEL));
addChannel("Switch", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_RO_SWITCH),
defaultChannelLabel + " / Evaporator / Out Of Water", "Evaporator Out Of Water", tags, null,
() -> getStateOnOffValue(STATE_OUT_OF_WATER));
addChannel("Number", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_RO_NUMBER),
defaultChannelLabel + " / Evaporator / Humidity / Actual", "Actual Humidity", tags, null,
() -> getStateDecimalValue(STATE_HUMIDITY_ACTUAL));
addChannel("Number", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_NUMBER),
defaultChannelLabel + " / Evaporator / Humidity / Target", "Target Humidity", tags,
(cmd) -> handleSetNumberCommands(cmd, CMD_SET_HUMIDITY_TARGET),
() -> getStateDecimalValue(STATE_HUMIDITY_TARGET));
addChannel("Number", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_NUMBER),
defaultChannelLabel + " / Evaporator / Mode", "Evaporator Mode", tags, this::handleModeCommands,
() -> getStateDecimalValue(STATE_EVAPORATOR_MODE));
}
addChannel("Switch", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_SWITCH),
defaultChannelLabel + " / Next State", "Trigger Next State", tags,
(cmd) -> handleTriggerCommands(cmd, CMD_NEXT_STATE), () -> OnOffType.OFF);
}
private void handleSaunaActivateCommands(Command command) throws IOException {
if (command instanceof OnOffType) {
if ((OnOffType) command == OnOffType.ON) {
sendAction(CMD_ON);
} else {
sendAction(CMD_OFF);
}
}
}
private void handleSetNumberCommands(Command command, String prefix) throws IOException {
if (command instanceof DecimalType) {
Double value = ((DecimalType) command).doubleValue();
sendAction(prefix + value.toString());
}
}
private void handleFanCommands(Command command) throws IOException {
if (command instanceof OnOffType) {
if ((OnOffType) command == OnOffType.ON) {
sendAction(CMD_FAN_ON);
} else {
sendAction(CMD_FAN_OFF);
}
}
}
private void handleTriggerCommands(Command command, String prefix) throws IOException {
if (command instanceof OnOffType && (OnOffType) command == OnOffType.ON) {
sendAction(prefix);
}
}
private void handleModeCommands(Command command) throws IOException {
if (command instanceof DecimalType) {
Double value = ((DecimalType) command).doubleValue();
// per API there are 7 evaporator modes selected with number 0-6
if (value % 1 == 0 && value >= 0.0 && value <= 6.0) {
sendAction(CMD_SET_EVAPORATOR_MODE + value.toString());
}
}
}
private State getStateErrorValue() {
Double val = getStateDoubleValue(STATE_ERROR);
if (val != null && val != 0.0) {
return getStateDecimalValue(STATE_SAUNA_ERROR);
}
return DecimalType.ZERO;
}
}

View File

@ -0,0 +1,56 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.loxone.internal.controls;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.openhab.core.library.types.OnOffType;
/**
* Test class for (@link LxControlSauna} - version with door sensor no vaporizer
*
* @author Pawel Pieczul - initial contribution
*
*/
public class LxControlSaunaDoorTest extends LxControlSaunaTest {
@Override
@BeforeEach
public void setup() {
setupControl("17452951-02ae-1b6e-ffff266cf17271dc", "0b734138-037d-034e-ffff403fb0c34b9e",
"0fe650c2-0004-d446-ffff504f9410790f", "Sauna Controller No Vaporizer With Door Sensor");
}
@Override
@Test
public void testControlCreation() {
testControlCreation(LxControlSauna.class, 3, 0, 13, 13, 14);
}
@Override
@Test
public void testChannels() {
super.testChannels();
testChannel("Switch", DOOR_CLOSED_CHANNEL);
}
@Override
@Test
public void testDoorClosedChannel() {
for (int i = 0; i < 5; i++) {
changeLoxoneState("doorclosed", 0.0);
testChannelState(DOOR_CLOSED_CHANNEL, OnOffType.OFF);
changeLoxoneState("doorclosed", 1.0);
testChannelState(DOOR_CLOSED_CHANNEL, OnOffType.ON);
}
}
}

View File

@ -0,0 +1,123 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.loxone.internal.controls;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.types.UnDefType;
/**
* Test class for (@link LxControlSauna} - version with vaporizer and door sensor
*
* @author Pawel Pieczul - initial contribution
*
*/
public class LxControlSaunaDoorVaporizerTest extends LxControlSaunaDoorTest {
@Override
@BeforeEach
public void setup() {
setupControl("17452951-02ae-1b6e-ffff266cf17271dd", "0b734138-037d-034e-ffff403fb0c34b9e",
"0fe650c2-0004-d446-ffff504f9410790f", "Sauna Controller With Vaporizer With Door Sensor");
}
@Override
@Test
public void testControlCreation() {
testControlCreation(LxControlSauna.class, 3, 0, 18, 18, 21);
}
@Override
@Test
public void testChannels() {
super.testChannels();
testChannel("Number", VAPOR_POWER_CHANNEL);
testChannel("Switch", OUT_OF_WATER_CHANNEL);
testChannel("Number", ACTUAL_HUMIDITY_CHANNEL);
testChannel("Number", TARGET_HUMIDITY_CHANNEL);
testChannel("Number", EVAPORATOR_MODE_CHANNEL);
}
@Override
@Test
public void vaporPowerChannel() {
for (Double i = 0.0; i <= 100.0; i += 1.0) {
changeLoxoneState("vaporpower", i);
testChannelState(VAPOR_POWER_CHANNEL, new PercentType(i.intValue()));
}
changeLoxoneState("vaporpower", -1.0);
testChannelState(VAPOR_POWER_CHANNEL, UnDefType.UNDEF);
changeLoxoneState("vaporpower", 100.1);
testChannelState(VAPOR_POWER_CHANNEL, UnDefType.UNDEF);
}
@Override
@Test
public void testOutOfWaterChannel() {
for (int i = 0; i < 5; i++) {
changeLoxoneState("lesswater", 0.0);
testChannelState(OUT_OF_WATER_CHANNEL, OnOffType.OFF);
changeLoxoneState("lesswater", 1.0);
testChannelState(OUT_OF_WATER_CHANNEL, OnOffType.ON);
}
}
@Override
@Test
public void testActualHumidityChannel() {
for (Double i = 0.0; i <= 100.0; i += 0.17) {
changeLoxoneState("humidityactual", i);
testChannelState(ACTUAL_HUMIDITY_CHANNEL, new DecimalType(i));
}
}
@Override
@Test
public void testTargetHumidityChannel() {
for (Double i = 0.0; i <= 100.0; i += 0.17) {
changeLoxoneState("humiditytarget", i);
testChannelState(TARGET_HUMIDITY_CHANNEL, new DecimalType(i));
}
for (Double i = 0.0; i <= 100.0; i += 0.13) {
executeCommand(TARGET_HUMIDITY_CHANNEL, new DecimalType(i));
testAction("humidity/" + i.toString());
}
}
@Override
@Test
public void testEvaporatorModelChannel() {
for (Double i = 0.0; i <= 6.0; i += 1.0) {
changeLoxoneState("mode", i);
testChannelState(EVAPORATOR_MODE_CHANNEL, new DecimalType(i));
}
for (Double i = -10.0; i < 0.0; i += 0.4) {
executeCommand(EVAPORATOR_MODE_CHANNEL, new DecimalType(i));
testAction(null);
}
for (Double i = 0.0; i < 6.0; i += 1.0) {
executeCommand(EVAPORATOR_MODE_CHANNEL, new DecimalType(i));
testAction("mode/" + i.toString());
}
for (Double i = 6.1; i < 15.0; i += 0.1) {
executeCommand(EVAPORATOR_MODE_CHANNEL, new DecimalType(i));
testAction(null);
}
for (Double i = 0.3; i < 6.0; i += 1.0) {
executeCommand(EVAPORATOR_MODE_CHANNEL, new DecimalType(i));
testAction(null);
}
}
}

View File

@ -0,0 +1,238 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.loxone.internal.controls;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.types.UnDefType;
/**
* Test class for (@link LxControlSauna} - version with no door sensor and no vaporizer
*
* @author Pawel Pieczul - initial contribution
*
*/
public class LxControlSaunaTest extends LxControlTest {
private static final String ACTIVE_CHANNEL = " / Active";
private static final String POWER_CHANNEL = " / Power";
private static final String TEMP_ACTUAL_CHANNEL = " / Temperature / Actual";
private static final String TEMP_BENCH_CHANNEL = " / Temperature / Bench";
private static final String TEMP_TARGET_CHANNEL = " / Temperature / Target";
private static final String FAN_CHANNEL = " / Fan";
private static final String DRYING_CHANNEL = " / Drying";
static final String DOOR_CLOSED_CHANNEL = " / Door Closed";
private static final String ERROR_CODE_CHANNEL = " / Error Code";
static final String VAPOR_POWER_CHANNEL = " / Evaporator / Power";
private static final String TIMER_CURRENT_CHANNEL = " / Timer / Current";
private static final String TIMER_TRIGGER_CHANNEL = " / Timer / Trigger";
private static final String TIMER_TOTAL_CHANNEL = " / Timer / Total";
static final String OUT_OF_WATER_CHANNEL = " / Evaporator / Out Of Water";
static final String ACTUAL_HUMIDITY_CHANNEL = " / Evaporator / Humidity / Actual";
static final String TARGET_HUMIDITY_CHANNEL = " / Evaporator / Humidity / Target";
static final String EVAPORATOR_MODE_CHANNEL = " / Evaporator / Mode";
private static final String NEXT_STATE_CHANNEL = " / Next State";
@BeforeEach
public void setup() {
setupControl("17452951-02ae-1b6e-ffff266cf17271db", "0b734138-037d-034e-ffff403fb0c34b9e",
"0fe650c2-0004-d446-ffff504f9410790f", "Sauna Controller No Vaporizer No Door Sensor");
}
@Test
public void testControlCreation() {
testControlCreation(LxControlSauna.class, 3, 0, 12, 12, 14);
}
@Test
public void testChannels() {
testChannel("Switch", ACTIVE_CHANNEL);
testChannel("Number", POWER_CHANNEL);
testChannel("Number", TEMP_ACTUAL_CHANNEL);
testChannel("Number", TEMP_BENCH_CHANNEL);
testChannel("Number", TEMP_TARGET_CHANNEL);
testChannel("Switch", FAN_CHANNEL);
testChannel("Switch", DRYING_CHANNEL);
testChannel("Number", ERROR_CODE_CHANNEL);
testChannel("Number", TIMER_CURRENT_CHANNEL);
testChannel("Switch", TIMER_TRIGGER_CHANNEL);
testChannel("Number", TIMER_TOTAL_CHANNEL);
testChannel("Switch", NEXT_STATE_CHANNEL);
}
@Test
public void testActiveChannel() {
for (int i = 0; i < 5; i++) {
changeLoxoneState("active", 0.0);
testChannelState(ACTIVE_CHANNEL, OnOffType.OFF);
changeLoxoneState("active", 1.0);
testChannelState(ACTIVE_CHANNEL, OnOffType.ON);
}
for (int i = 0; i < 5; i++) {
executeCommand(ACTIVE_CHANNEL, OnOffType.ON);
testAction("on");
executeCommand(ACTIVE_CHANNEL, DecimalType.ZERO);
testAction(null);
executeCommand(ACTIVE_CHANNEL, OnOffType.OFF);
testAction("off");
executeCommand(ACTIVE_CHANNEL, StringType.EMPTY);
testAction(null);
}
}
@Test
public void testPowerChannel() {
for (Double i = 0.0; i <= 100.0; i += 1.0) {
changeLoxoneState("power", i);
testChannelState(POWER_CHANNEL, new PercentType(i.intValue()));
}
changeLoxoneState("power", -1.0);
testChannelState(POWER_CHANNEL, UnDefType.UNDEF);
changeLoxoneState("power", 100.1);
testChannelState(POWER_CHANNEL, UnDefType.UNDEF);
}
@Test
public void testTempActualBenchChannels() {
for (Double i = -20.0; i <= 150.0; i += 0.37) {
changeLoxoneState("tempactual", i);
testChannelState(TEMP_ACTUAL_CHANNEL, new DecimalType(i));
changeLoxoneState("tempbench", i * 1.1);
testChannelState(TEMP_BENCH_CHANNEL, new DecimalType(i * 1.1));
changeLoxoneState("temptarget", i * 1.2);
testChannelState(TEMP_TARGET_CHANNEL, new DecimalType(i * 1.2));
}
}
@Test
public void testTempTargetSetCommand() {
for (Double i = 0.0; i <= 150.0; i += 0.37) {
executeCommand(TEMP_TARGET_CHANNEL, new DecimalType(i));
testAction("temp/" + i.toString());
}
}
@Test
public void testFanChannel() {
for (int i = 0; i < 5; i++) {
changeLoxoneState("fan", 0.0);
testChannelState(FAN_CHANNEL, OnOffType.OFF);
changeLoxoneState("fan", 1.0);
testChannelState(FAN_CHANNEL, OnOffType.ON);
}
for (int i = 0; i < 5; i++) {
executeCommand(FAN_CHANNEL, OnOffType.ON);
testAction("fanon");
executeCommand(FAN_CHANNEL, DecimalType.ZERO);
testAction(null);
executeCommand(FAN_CHANNEL, OnOffType.OFF);
testAction("fanoff");
executeCommand(FAN_CHANNEL, StringType.EMPTY);
testAction(null);
}
}
@Test
public void testDryingChannel() {
for (int i = 0; i < 5; i++) {
changeLoxoneState("drying", 0.0);
testChannelState(DRYING_CHANNEL, OnOffType.OFF);
changeLoxoneState("drying", 1.0);
testChannelState(DRYING_CHANNEL, OnOffType.ON);
}
}
@Test
public void testDoorClosedChannel() {
testNoChannel(DOOR_CLOSED_CHANNEL);
}
@Test
public void testErrorCodeChannel() {
for (Double i = 0.0; i < 10.0; i += 1.0) {
changeLoxoneState("saunaerror", i);
changeLoxoneState("error", 0.0);
testChannelState(ERROR_CODE_CHANNEL, DecimalType.ZERO);
changeLoxoneState("error", 1.0);
testChannelState(ERROR_CODE_CHANNEL, new DecimalType(i));
}
}
@Test
public void testTimerCurrentTotalChannels() {
for (Double i = 0.0; i <= 150.0; i += 0.21) {
changeLoxoneState("timer", i);
testChannelState(TIMER_CURRENT_CHANNEL, new DecimalType(i));
changeLoxoneState("timertotal", i * 1.3);
testChannelState(TIMER_TOTAL_CHANNEL, new DecimalType(i * 1.3));
}
}
@Test
public void testTimerTriggerChannel() {
for (int i = 0; i <= 10; i++) {
executeCommand(TIMER_TRIGGER_CHANNEL, DecimalType.ZERO);
testAction(null);
testChannelState(TIMER_TRIGGER_CHANNEL, OnOffType.OFF);
executeCommand(TIMER_TRIGGER_CHANNEL, OnOffType.ON);
testAction("starttimer");
testChannelState(TIMER_TRIGGER_CHANNEL, OnOffType.OFF);
executeCommand(TIMER_TRIGGER_CHANNEL, OnOffType.OFF);
testAction(null);
testChannelState(TIMER_TRIGGER_CHANNEL, OnOffType.OFF);
}
}
@Test
public void vaporPowerChannel() {
testNoChannel(VAPOR_POWER_CHANNEL);
}
@Test
public void testOutOfWaterChannel() {
testNoChannel(OUT_OF_WATER_CHANNEL);
}
@Test
public void testActualHumidityChannel() {
testNoChannel(ACTUAL_HUMIDITY_CHANNEL);
}
@Test
public void testTargetHumidityChannel() {
testNoChannel(TARGET_HUMIDITY_CHANNEL);
}
@Test
public void testEvaporatorModelChannel() {
testNoChannel(EVAPORATOR_MODE_CHANNEL);
}
@Test
public void testNextStateTriggerChannel() {
for (int i = 0; i <= 10; i++) {
executeCommand(NEXT_STATE_CHANNEL, DecimalType.ZERO);
testAction(null);
testChannelState(NEXT_STATE_CHANNEL, OnOffType.OFF);
executeCommand(NEXT_STATE_CHANNEL, OnOffType.ON);
testAction("pulse");
testChannelState(NEXT_STATE_CHANNEL, OnOffType.OFF);
executeCommand(NEXT_STATE_CHANNEL, OnOffType.OFF);
testAction(null);
testChannelState(NEXT_STATE_CHANNEL, OnOffType.OFF);
}
}
}

View File

@ -147,6 +147,13 @@ class LxControlTest {
testChannel(itemType, namePostFix, null, null, null, null, null, null, null);
}
void testNoChannel(String namePostFix) {
LxControl ctrl = getControl(controlUuid);
assertNotNull(ctrl);
Channel c = getChannel(getExpectedName(ctrl.getLabel(), ctrl.getRoom().getName(), namePostFix), ctrl);
assertNull(c);
}
void testChannel(String itemType, String namePostFix, Set<String> tags) {
testChannel(itemType, namePostFix, null, null, null, null, null, null, tags);
}
@ -249,8 +256,10 @@ class LxControlTest {
private Channel getChannel(String name, LxControl c) {
List<Channel> channels = c.getChannels();
List<Channel> filtered = channels.stream().filter(a -> name.equals(a.getLabel())).collect(Collectors.toList());
assertEquals(1, filtered.size());
return filtered.get(0);
if (filtered.size() == 1) {
return filtered.get(0);
}
return null;
}
private <T> long numberOfControls(Class<T> c) {

View File

@ -851,8 +851,108 @@
}
}
}
}
},
},
"17452951-02ae-1b6e-ffff266cf17271db": {
"name": "Sauna Controller No Vaporizer No Door Sensor",
"type": "Sauna",
"uuidAction": "17452951-02ae-1b6e-ffff266cf17271db",
"room": "0b734138-037d-034e-ffff403fb0c34b9e",
"cat": "0fe650c2-0004-d446-ffff504f9410790f",
"defaultRating": 0,
"isFavorite": false,
"isSecured": false,
"details": {
"jLockable": true,
"hasVaporizer": false,
"hasDoorSensor": false
},
"states": {
"jLocked": "97452951-02ae-1b57-ffffe29abab51e83",
"power": "17452951-02ae-1b62-ffffe29abab51e83",
"tempActual": "17452951-02ae-1b51-ffffe29abab51e83",
"tempBench": "17452951-02ae-1b55-ffffe29abab51e83",
"tempTarget": "17452951-02ae-1b6b-ffffe29abab51e83",
"fan": "17452951-02ae-1b67-ffffe29abab51e83",
"drying": "17452951-02ae-1b69-ffffe29abab51e83",
"doorClosed": "17452951-02ae-1b54-ffffe29abab51e83",
"presence": "17452951-02ae-1b56-ffffe29abab51e83",
"error": "17452951-02ae-1b6a-ffffe29abab51e83",
"saunaError": "17452951-02ae-1b4e-ffffe29abab51e83",
"timer": "17452951-02ae-1b68-ffffe29abab51e83",
"active": "17452951-02ae-1b66-ffffe29abab51e83",
"timerTotal": "17452951-02ae-1b5c-ffffe29abab51e83"
}
},
"17452951-02ae-1b6e-ffff266cf17271dc": {
"name": "Sauna Controller No Vaporizer With Door Sensor",
"type": "Sauna",
"uuidAction": "17452951-02ae-1b6e-ffff266cf17271dc",
"room": "0b734138-037d-034e-ffff403fb0c34b9e",
"cat": "0fe650c2-0004-d446-ffff504f9410790f",
"defaultRating": 0,
"isFavorite": false,
"isSecured": false,
"details": {
"jLockable": true,
"hasVaporizer": false,
"hasDoorSensor": true
},
"states": {
"jLocked": "97452951-02ae-1b57-ffffe29abab51e84",
"power": "17452951-02ae-1b62-ffffe29abab51e84",
"tempActual": "17452951-02ae-1b51-ffffe29abab51e84",
"tempBench": "17452951-02ae-1b55-ffffe29abab51e84",
"tempTarget": "17452951-02ae-1b6b-ffffe29abab51e84",
"fan": "17452951-02ae-1b67-ffffe29abab51e84",
"drying": "17452951-02ae-1b69-ffffe29abab51e84",
"doorClosed": "17452951-02ae-1b54-ffffe29abab51e84",
"presence": "17452951-02ae-1b56-ffffe29abab51e84",
"error": "17452951-02ae-1b6a-ffffe29abab51e84",
"saunaError": "17452951-02ae-1b4e-ffffe29abab51e84",
"timer": "17452951-02ae-1b68-ffffe29abab51e84",
"active": "17452951-02ae-1b66-ffffe29abab51e84",
"timerTotal": "17452951-02ae-1b5c-ffffe29abab51e84"
}
},
"17452951-02ae-1b6e-ffff266cf17271dd": {
"name": "Sauna Controller With Vaporizer With Door Sensor",
"type": "Sauna",
"uuidAction": "17452951-02ae-1b6e-ffff266cf17271dd",
"room": "0b734138-037d-034e-ffff403fb0c34b9e",
"cat": "0fe650c2-0004-d446-ffff504f9410790f",
"defaultRating": 0,
"isFavorite": false,
"isSecured": false,
"details": {
"jLockable": true,
"hasVaporizer": true,
"hasDoorSensor": true
},
"states": {
"jLocked": "97452951-02ae-1b57-ffffe29abab51e85",
"power": "17452951-02ae-1b62-ffffe29abab51e85",
"tempActual": "17452951-02ae-1b51-ffffe29abab51e85",
"tempBench": "17452951-02ae-1b55-ffffe29abab51e85",
"tempTarget": "17452951-02ae-1b6b-ffffe29abab51e85",
"fan": "17452951-02ae-1b67-ffffe29abab51e85",
"drying": "17452951-02ae-1b69-ffffe29abab51e85",
"doorClosed": "17452951-02ae-1b54-ffffe29abab51e85",
"presence": "17452951-02ae-1b56-ffffe29abab51e85",
"error": "17452951-02ae-1b6a-ffffe29abab51e85",
"vaporPower": "17eb161f-0350-6d90-ffff292cf0ed07b9",
"saunaError": "17452951-02ae-1b4e-ffffe29abab51e85",
"tempAndHumidity": "17eb161f-0350-6d6d-ffff292cf0ed07b9",
"ready": "17eb161f-0350-6d6e-ffff292cf0ed07b9",
"timer": "17452951-02ae-1b68-ffffe29abab51e85",
"active": "17452951-02ae-1b66-ffffe29abab51e85",
"lessWater": "17eb161f-0350-6d80-ffff292cf0ed07b9",
"humidityActual": "17eb161f-0350-6d7a-ffff292cf0ed07b9",
"humidityTarget": "17eb161f-0350-6d99-ffff292cf0ed07b9",
"mode": "17eb161f-0350-6d96-ffff292cf0ed07b9",
"timerTotal": "17452951-02ae-1b5c-ffffe29abab51e85"
}
}
},
"weatherServer": {
"states": {
"actual": "0b734139-0012-04f4-ffff403fb0c34b9e",