2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.loxone.internal.controls;
15 import static org.hamcrest.CoreMatchers.*;
16 import static org.hamcrest.MatcherAssert.assertThat;
17 import static org.junit.jupiter.api.Assertions.*;
19 import java.io.BufferedReader;
20 import java.io.InputStream;
21 import java.io.InputStreamReader;
22 import java.math.BigDecimal;
23 import java.util.ArrayList;
24 import java.util.List;
26 import java.util.stream.Collectors;
28 import org.junit.jupiter.api.BeforeEach;
29 import org.junit.jupiter.api.Test;
30 import org.openhab.binding.loxone.internal.types.LxUuid;
31 import org.openhab.core.library.types.DecimalType;
32 import org.openhab.core.library.types.OnOffType;
33 import org.openhab.core.library.types.UpDownType;
34 import org.openhab.core.types.StateOption;
35 import org.openhab.core.types.UnDefType;
38 * Test class for (@link LxControlSwitch}
40 * @author Pawel Pieczul - initial contribution
43 public class LxControlLightControllerV2Test extends LxControlTest {
46 setupControl("1076668f-0101-7076-ffff403fb0c34b9e", "0b734138-03ac-03f0-ffff403fb0c34b9e",
47 "0b734138-033e-02d4-ffff403fb0c34b9e", "Lighting controller");
51 public void testControlCreation() {
52 testControlCreation(LxControlLightControllerV2.class, 1, 5, 1, 6, 4);
53 testSubControl(LxControlSwitch.class, "Overall Switch");
54 testSubControl(LxControlSwitch.class, "Hall Study Lights");
55 testSubControl(LxControlSwitch.class, "Roof Lights");
56 testSubControl(LxControlSwitch.class, "Hall Left Lights");
57 testSubControl(LxControlSwitch.class, "Hall Right Lights");
61 public void testChannels() {
62 testChannel("Number", Set.of("Scene"));
66 public void testCommands() {
67 for (int i = 0; i < 20; i++) {
68 executeCommand(UpDownType.UP);
70 executeCommand(UpDownType.DOWN);
73 executeCommand(new DecimalType(1));
78 executeCommand(new DecimalType(0));
80 executeCommand(new DecimalType(1));
82 executeCommand(new DecimalType(2));
83 testAction("changeTo/2");
84 executeCommand(new DecimalType(3));
85 testAction("changeTo/3");
86 executeCommand(new DecimalType(4));
87 testAction("changeTo/4");
88 executeCommand(new DecimalType(5));
89 testAction("changeTo/5");
90 executeCommand(new DecimalType(6));
92 executeCommand(new DecimalType(777));
93 testAction("changeTo/777");
94 executeCommand(new DecimalType(778));
95 testAction("changeTo/778");
96 executeCommand(new DecimalType(779));
102 public void testMoodListChanges() {
103 for (int i = 0; i < 20; i++) {
111 public void testActiveMoodChanges() {
113 for (int i = 0; i < 10; i++) {
114 changeLoxoneState("activemoods", "[4]");
115 testActiveMoods(779, 4);
116 changeLoxoneState("activemoods", "[4,6]");
117 testActiveMoods(779, 4, 6);
118 changeLoxoneState("activemoods", "[4,6,7,8,5]");
119 testActiveMoods(779, 4, 6, 7, 8, 5);
120 changeLoxoneState("activemoods", "[4,6,7,8,5,777,778]");
121 testActiveMoods(779, 4, 6, 7, 8, 5, 777, 778);
122 changeLoxoneState("activemoods", "[6,8,777]");
123 testActiveMoods(779, 6, 8, 777);
124 changeLoxoneState("activemoods", "[779]");
125 testActiveMoods(779, 779);
126 changeLoxoneState("activemoods", "[1,2,3,500,900]");
127 testActiveMoods(779);
128 changeLoxoneState("activemoods", "[1,2,3,6,500,900]");
129 testActiveMoods(779, 6);
130 changeLoxoneState("activemoods", "[5]");
131 testActiveMoods(779, 5);
132 changeLoxoneState("activemoods", "[778]");
133 testActiveMoods(779, 778);
139 public void testMoodAddRemove() {
141 for (int i = 0; i < 10; i++) {
142 handler.extraControls.values().forEach(c -> {
143 LxControlMood m = (LxControlMood) c;
144 if (!m.getId().equals(778)) {
145 executeCommand(m, OnOffType.ON);
146 testAction("addMood/" + m.getId());
147 executeCommand(m, OnOffType.OFF);
148 testAction("removeMood/" + m.getId());
155 private void testActiveMoods(Integer offId, Integer... moods) {
156 List<Integer> ids = new ArrayList<>();
157 for (Integer id : moods) {
158 if (!offId.equals(id)) {
159 LxControlMood m = getMood(id);
160 testChannelState(m, OnOffType.ON);
164 handler.extraControls.values().stream()
165 .filter(c -> !ids.contains(((LxControlMood) c).getId()) && !((LxControlMood) c).getId().equals(offId))
166 .forEach(c -> testChannelState(c, OnOffType.OFF));
168 if (ids.size() == 1) {
169 testChannelState(new DecimalType(ids.get(0)));
171 testChannelState(UnDefType.UNDEF);
175 private void loadMoodList1() {
176 String list = loadMoodList("MoodList1.json");
177 changeLoxoneState("moodlist", list);
178 List<StateOption> options = new ArrayList<>();
179 options.add(new StateOption("2", "Side Lights"));
180 options.add(new StateOption("3", "Play Lights"));
181 options.add(new StateOption("4", "Study Only"));
182 options.add(new StateOption("5", "Low Lights"));
183 options.add(new StateOption("777", "Bright"));
184 options.add(new StateOption("778", "Off"));
185 testMoodList(options, 778);
188 private void loadMoodList2() {
189 String list = loadMoodList("MoodList2.json");
190 changeLoxoneState("moodlist", list);
191 List<StateOption> options = new ArrayList<>();
192 options.add(new StateOption("4", "Study Only Changed Name")); // changed name
193 options.add(new StateOption("5", "Low Lights")); // same as in list 1
194 options.add(new StateOption("6", "Play Lights")); // changed id
195 options.add(new StateOption("7", "New Mood 1"));
196 options.add(new StateOption("8", "New Mood 2"));
197 options.add(new StateOption("777", "Bright"));
198 options.add(new StateOption("778", "Off"));
199 options.add(new StateOption("779", "New Off"));
200 testMoodList(options, 779);
203 private LxControlMood getMood(Integer id) {
204 LxControl ctrl = handler.extraControls.get(new LxUuid("1076668f-0101-7076-ffff403fb0c34b9e-M" + id));
206 assertThat(ctrl, is(instanceOf(LxControlMood.class)));
207 return (LxControlMood) ctrl;
210 private void clearMoodList() {
211 changeLoxoneState("moodlist", "[]");
212 List<StateOption> options = new ArrayList<>();
213 testMoodList(options, 0);
216 private void testMoodList(List<StateOption> options, Integer offId) {
217 assertEquals(options.size(), handler.extraControls.size());
218 if (options.isEmpty()) {
223 for (StateOption o : options) {
224 testMood(o.getLabel(), o.getValue(), o.getValue().equals(offId.toString()));
225 Integer id = Integer.parseInt(o.getValue());
227 if (min == null || id < min) {
230 if (max == null || id > max) {
236 testChannel("Number", null, new BigDecimal(min), new BigDecimal(max), BigDecimal.ONE, null, false, options,
240 private void testMood(String name, String id, boolean isStatic) {
241 LxControlMood mood = getMood(Integer.parseInt(id));
242 assertEquals(new LxUuid("0b734138-03ac-03f0-ffff403fb0c34b9e"), mood.getRoom().getUuid());
243 assertEquals(new LxUuid("0b734138-033e-02d4-ffff403fb0c34b9e"), mood.getCategory().getUuid());
244 assertEquals(name, mood.getName());
245 assertEquals(id, mood.getId().toString());
247 assertEquals(0, mood.getChannels().size());
249 assertEquals(1, mood.getChannels().size());
250 testChannel(mood, "Switch", Set.of("Lighting"));
254 private String loadMoodList(String name) {
255 InputStream stream = LxControlLightControllerV2Test.class.getResourceAsStream(name);
256 assertNotNull(stream);
257 BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
258 assertNotNull(reader);
259 String msg = reader.lines().collect(Collectors.joining(System.lineSeparator()));
261 // mood list comes as a single line JSON from Loxone Miniserver
262 msg = msg.replaceAll("[\\t+\\n+]", "");