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;
Map<String, String> outputs;
Boolean presenceConnected;
Integer connectedInputs;
+ Boolean hasVaporizer;
+ Boolean hasDoorSensor;
}
/**
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
*
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());
--- /dev/null
+/**
+ * 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;
+ }
+}
--- /dev/null
+/**
+ * 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);
+ }
+ }
+}
--- /dev/null
+/**
+ * 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);
+ }
+ }
+}
--- /dev/null
+/**
+ * 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);
+ }
+ }
+}
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);
}
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) {
}
}
}
- }
- },
+ },
+ "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",