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.velux.test;
15 import static org.junit.jupiter.api.Assertions.*;
17 import org.eclipse.jdt.annotation.NonNullByDefault;
18 import org.junit.jupiter.api.MethodOrderer;
19 import org.junit.jupiter.api.Order;
20 import org.junit.jupiter.api.Test;
21 import org.junit.jupiter.api.TestMethodOrder;
22 import org.openhab.binding.velux.internal.bridge.slip.FunctionalParameters;
23 import org.openhab.binding.velux.internal.bridge.slip.SCgetHouseStatus;
24 import org.openhab.binding.velux.internal.bridge.slip.SCgetProduct;
25 import org.openhab.binding.velux.internal.bridge.slip.SCgetProductStatus;
26 import org.openhab.binding.velux.internal.bridge.slip.SCrunProductCommand;
27 import org.openhab.binding.velux.internal.things.VeluxExistingProducts;
28 import org.openhab.binding.velux.internal.things.VeluxKLFAPI;
29 import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command;
30 import org.openhab.binding.velux.internal.things.VeluxProduct;
31 import org.openhab.binding.velux.internal.things.VeluxProduct.ProductBridgeIndex;
32 import org.openhab.binding.velux.internal.things.VeluxProduct.ProductState;
33 import org.openhab.binding.velux.internal.things.VeluxProductName;
34 import org.openhab.binding.velux.internal.things.VeluxProductPosition;
35 import org.openhab.binding.velux.internal.things.VeluxProductPosition.PositionType;
36 import org.openhab.binding.velux.internal.things.VeluxProductType.ActuatorType;
37 import org.openhab.core.library.types.PercentType;
40 * JUnit test suite to check the proper parsing of actuator notification packets, and to confirm that the existing
41 * products database is working correctly.
43 * @author Andrew Fiddian-Green - Initial contribution.
46 @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
47 public class TestNotificationsAndDatabase {
48 // validation parameters
49 private static final byte PRODUCT_INDEX_A = 6;
50 private static final byte PRODUCT_INDEX_B = 0;
51 private static final int MAIN_POSITION_A = 0xC800;
52 private static final int MAIN_POSITION_B = 0x4600;
53 private static final int VANE_POSITION_A = 0x634f;
54 private static final int TARGET_POSITION = 0xB800;
55 private static final int STATE_SOMFY = 0x2D;
57 private static final int UNKNOWN_POSITION = VeluxProductPosition.VPP_VELUX_UNKNOWN;
58 private static final int IGNORE_POSITION = VeluxProductPosition.VPP_VELUX_IGNORE;
59 private static final int STATE_DONE = VeluxProduct.ProductState.DONE.value;
61 private static final ActuatorType ACTUATOR_TYPE_SOMFY = ActuatorType.BLIND_17;
62 private static final ActuatorType ACTUATOR_TYPE_VELUX = ActuatorType.WINDOW_4_1;
63 private static final ActuatorType ACTUATOR_TYPE_UNDEF = ActuatorType.UNDEFTYPE;
65 // existing products database
66 private static final VeluxExistingProducts EXISTING_PRODUCTS = new VeluxExistingProducts();
68 private static byte[] toByteArray(String input) {
69 String[] data = input.split(" ");
70 byte[] result = new byte[data.length];
71 for (int i = 0; i < data.length; i++) {
72 result[i] = Integer.decode("0x" + data[i]).byteValue();
77 private VeluxExistingProducts getExistingProducts() {
78 return EXISTING_PRODUCTS;
82 * Confirm the existing products database is initialised.
86 public void testInitialized() {
87 assertEquals(0, getExistingProducts().getNoMembers());
91 * Test the 'supportsVanePosition()' method for two types of products.
95 public void testSupportsVanePosition() {
96 VeluxProduct product = new VeluxProduct();
97 assertFalse(product.supportsVanePosition());
98 product.setActuatorType(ACTUATOR_TYPE_SOMFY);
99 assertTrue(product.supportsVanePosition());
103 * Test the SCgetProduct command by checking for the correct parsing of a 'GW_GET_NODE_INFORMATION_NTF' notification
104 * packet. Note: this packet is from a Somfy roller shutter with main and vane position.
108 public void testSCgetProduct() {
109 // initialise the test parameters
110 final String packet = """
111 06 00 06 00 48 6F 62 62 79 6B 61 6D 65 72 00 00 00 00 00 00 00 00 00 00 00 00 00 00\
112 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\
113 00 00 00 00 00 01 04 40 00 00 00 00 00 00 00 00 00 00 00 00 00 2D C8 00 C8 00 F7 FF F7 FF 00 00 F7 FF 00\
114 00 4F 00 4A EA 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\
116 final Command command = VeluxKLFAPI.Command.GW_GET_NODE_INFORMATION_NTF;
118 // initialise the BCP
119 SCgetProduct bcp = new SCgetProduct();
120 bcp.setProductId(PRODUCT_INDEX_A);
122 // set the packet response
123 bcp.setResponse(command.getShort(), toByteArray(packet), false);
126 assertTrue(bcp.isCommunicationSuccessful());
127 assertTrue(bcp.isCommunicationFinished());
129 // initialise the product
130 VeluxProduct product = bcp.getProduct();
132 // check positive assertions
133 assertEquals(STATE_SOMFY, product.getState());
134 assertEquals(MAIN_POSITION_A, product.getCurrentPosition());
135 assertEquals(MAIN_POSITION_A, product.getTarget());
136 assertEquals(PRODUCT_INDEX_A, product.getBridgeProductIndex().toInt());
137 assertEquals(ACTUATOR_TYPE_SOMFY, product.getActuatorType());
138 assertEquals(UNKNOWN_POSITION, product.getVanePosition());
139 assertNull(product.getFunctionalParameters());
140 assertTrue(product.supportsVanePosition());
141 assertTrue(product.isSomfyProduct());
142 assertEquals(ProductState.DONE, product.getProductState());
144 // check negative assertions
145 assertNotEquals(VANE_POSITION_A, product.getVanePosition());
147 // register in existing products database
148 VeluxExistingProducts existingProducts = getExistingProducts();
149 assertTrue(existingProducts.register(product));
150 assertTrue(existingProducts.isRegistered(product));
151 assertTrue(existingProducts.isRegistered(product.getBridgeProductIndex()));
152 assertEquals(1, existingProducts.getNoMembers());
154 // confirm that a dummy product is NOT in the database
155 assertFalse(existingProducts.isRegistered(new ProductBridgeIndex(99)));
158 assertTrue(existingProducts.isDirty());
159 existingProducts.resetDirtyFlag();
160 assertFalse(existingProducts.isDirty());
162 // re-registering the same product should return false
163 assertFalse(existingProducts.register(product));
165 // updating again with the same data should NOT set the dirty flag
166 assertTrue(existingProducts.update(product));
167 assertFalse(existingProducts.isDirty());
169 // check that the product in the database is indeed the one just created
170 VeluxProduct existing = existingProducts.get(new ProductBridgeIndex(PRODUCT_INDEX_A));
171 assertEquals(product, existing);
172 assertEquals(1, existingProducts.getNoMembers());
173 assertTrue(existingProducts.isRegistered(product.getBridgeProductIndex()));
177 * Confirm that the product in the existing database has the same values as the product created and added in test 3.
181 public void testExistingUnknownVanePosition() {
182 VeluxExistingProducts existingProducts = getExistingProducts();
183 VeluxProduct product = existingProducts.get(new ProductBridgeIndex(PRODUCT_INDEX_A));
185 // confirm the product details
186 assertEquals(STATE_SOMFY, product.getState());
187 assertEquals(MAIN_POSITION_A, product.getCurrentPosition());
188 assertEquals(MAIN_POSITION_A, product.getTarget());
189 assertEquals(PRODUCT_INDEX_A, product.getBridgeProductIndex().toInt());
190 assertEquals(ACTUATOR_TYPE_SOMFY, product.getActuatorType());
191 assertEquals(UNKNOWN_POSITION, product.getVanePosition());
192 assertNull(product.getFunctionalParameters());
196 * Test the SCgetProductStatus command by checking for the correct parsing of a 'GW_STATUS_REQUEST_NTF' notification
197 * packet. Note: this packet is from a Somfy roller shutter with main and vane position.
201 public void testSCgetProductStatus() {
202 // initialise the test parameters
203 final String packet = """
204 00 D8 01 06 00 01 01 02 00 C8 00 03 63 4F 00 00 00 00 00 00 00 00 00 00 00 00 00\
205 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\
207 final Command command = VeluxKLFAPI.Command.GW_STATUS_REQUEST_NTF;
209 // initialise the BCP
210 SCgetProductStatus bcp = new SCgetProductStatus();
211 bcp.setProductId(PRODUCT_INDEX_A);
213 // set the packet response
214 bcp.setResponse(command.getShort(), toByteArray(packet), false);
217 assertTrue(bcp.isCommunicationSuccessful());
218 assertTrue(bcp.isCommunicationFinished());
220 // initialise the product
221 VeluxProduct product = bcp.getProduct();
223 // change actuator type
224 assertEquals(ACTUATOR_TYPE_UNDEF, product.getActuatorType());
225 product.setActuatorType(ACTUATOR_TYPE_SOMFY);
227 // check positive assertions
228 assertEquals(STATE_DONE, product.getState());
229 assertEquals(MAIN_POSITION_A, product.getCurrentPosition());
230 assertEquals(IGNORE_POSITION, product.getTarget());
231 assertEquals(PRODUCT_INDEX_A, product.getBridgeProductIndex().toInt());
232 assertEquals(ACTUATOR_TYPE_SOMFY, product.getActuatorType());
233 assertEquals(VANE_POSITION_A, product.getVanePosition());
234 assertNotNull(product.getFunctionalParameters());
235 assertEquals(ProductState.DONE, product.getProductState());
237 // test updating the existing product in the database
238 VeluxExistingProducts existingProducts = getExistingProducts();
239 assertTrue(existingProducts.update(product));
240 assertTrue(existingProducts.isDirty());
242 // updating again with the same data should NOT set the dirty flag
243 existingProducts.resetDirtyFlag();
244 assertTrue(existingProducts.update(product));
245 assertFalse(existingProducts.isDirty());
249 * Confirm that the product in the existing database has the same values as the product created and updated to the
250 * database in test 5.
254 public void testExistingValidVanePosition() {
255 VeluxExistingProducts existingProducts = getExistingProducts();
256 VeluxProduct product = existingProducts.get(new ProductBridgeIndex(PRODUCT_INDEX_A));
258 // confirm the product details
259 assertEquals(STATE_DONE, product.getState());
260 assertEquals(MAIN_POSITION_A, product.getCurrentPosition());
261 assertEquals(MAIN_POSITION_A, product.getTarget());
262 assertEquals(PRODUCT_INDEX_A, product.getBridgeProductIndex().toInt());
263 assertEquals(ACTUATOR_TYPE_SOMFY, product.getActuatorType());
264 assertEquals(VANE_POSITION_A, product.getVanePosition());
265 assertNotNull(product.getFunctionalParameters());
266 assertEquals(ProductState.DONE, product.getProductState());
270 * Test the SCgetHouseStatus command by checking for the correct parsing of a 'GW_NODE_STATE_POSITION_CHANGED_NTF'
271 * notification packet. Note: this packet is from a Somfy roller shutter with main and vane position.
275 public void testSCgetHouseStatus() {
276 // initialise the test parameters
277 final String packet = "06 2D C8 00 B8 00 F7 FF F7 FF 00 00 F7 FF 00 00 4A E5 00 00";
278 final short command = VeluxKLFAPI.Command.GW_NODE_STATE_POSITION_CHANGED_NTF.getShort();
280 // initialise the BCP
281 SCgetHouseStatus bcp = new SCgetHouseStatus();
283 // set the packet response
284 bcp.setResponse(command, toByteArray(packet), false);
287 assertTrue(bcp.isCommunicationSuccessful());
288 assertTrue(bcp.isCommunicationFinished());
290 // initialise the product
291 VeluxProduct product = bcp.getProduct();
293 // change actuator type
294 assertEquals(ACTUATOR_TYPE_UNDEF, product.getActuatorType());
295 product.setActuatorType(ACTUATOR_TYPE_SOMFY);
297 // check positive assertions
298 assertEquals(STATE_SOMFY, product.getState());
299 assertEquals(MAIN_POSITION_A, product.getCurrentPosition());
300 assertEquals(TARGET_POSITION, product.getTarget());
301 assertEquals(PRODUCT_INDEX_A, product.getBridgeProductIndex().toInt());
302 assertEquals(ACTUATOR_TYPE_SOMFY, product.getActuatorType());
303 assertEquals(UNKNOWN_POSITION, product.getVanePosition());
304 assertNull(product.getFunctionalParameters());
306 // check negative assertions
307 assertNotEquals(VANE_POSITION_A, product.getVanePosition());
309 VeluxExistingProducts existingProducts = getExistingProducts();
310 existingProducts.update(product);
314 * Confirm that the product in the existing database has the same values as the product created and updated to the
315 * database in test 7.
319 public void testExistingValidVanePositionWithNewTargetValue() {
320 VeluxExistingProducts existingProducts = getExistingProducts();
321 VeluxProduct product = existingProducts.get(new ProductBridgeIndex(PRODUCT_INDEX_A));
323 // confirm the product details
324 assertEquals(STATE_SOMFY, product.getState());
325 assertEquals(MAIN_POSITION_A, product.getCurrentPosition());
326 assertEquals(TARGET_POSITION, product.getTarget());
327 assertEquals(PRODUCT_INDEX_A, product.getBridgeProductIndex().toInt());
328 assertEquals(ACTUATOR_TYPE_SOMFY, product.getActuatorType());
329 assertEquals(VANE_POSITION_A, product.getVanePosition());
330 assertNotNull(product.getFunctionalParameters());
334 * Test the SCgetProduct by checking for the correct parsing of a 'GW_GET_NODE_INFORMATION_NTF' notification packet.
335 * Note: this packet is from a Velux roof window without vane position.
339 public void testSCgetProductOnVelux() {
340 // initialise the test parameters
341 final String packet = """
342 00 00 00 00 53 68 65 64 20 57 69 6E 64 6F 77 00 00 00 00 00 00 00 00 00 00 00 00 00\
343 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\
344 00 00 00 00 00 00 00 00 01 01 01 03 07 00 01 16 56 24 5C 26 14 19 00 FC 05 46 00 46 00 F7 FF F7\
345 FF F7 FF F7 FF 00 00 4F 05 B3 5F 01 D8 03 B2 1C 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\
347 final short command = VeluxKLFAPI.Command.GW_GET_NODE_INFORMATION_NTF.getShort();
349 // initialise the BCP
350 SCgetProduct bcp = new SCgetProduct();
351 bcp.setProductId(PRODUCT_INDEX_B);
353 // set the packet response
354 bcp.setResponse(command, toByteArray(packet), false);
357 assertTrue(bcp.isCommunicationSuccessful());
358 assertTrue(bcp.isCommunicationFinished());
360 // initialise the product
361 VeluxProduct product = bcp.getProduct();
363 // check positive assertions
364 assertEquals(STATE_DONE, product.getState());
365 assertEquals(MAIN_POSITION_B, product.getCurrentPosition());
366 assertEquals(MAIN_POSITION_B, product.getTarget());
367 assertEquals(PRODUCT_INDEX_B, product.getBridgeProductIndex().toInt());
368 assertEquals(ACTUATOR_TYPE_VELUX, product.getActuatorType());
369 assertEquals(UNKNOWN_POSITION, product.getVanePosition());
370 assertNull(product.getFunctionalParameters());
371 assertFalse(product.isSomfyProduct());
373 // check negative assertions
374 assertFalse(product.supportsVanePosition());
375 assertNotEquals(VANE_POSITION_A, product.getVanePosition());
377 // register in existing products database
378 VeluxExistingProducts existingProducts = getExistingProducts();
379 assertTrue(existingProducts.register(product));
380 assertTrue(existingProducts.isRegistered(product));
381 assertTrue(existingProducts.isRegistered(product.getBridgeProductIndex()));
382 assertEquals(2, existingProducts.getNoMembers());
384 // check that the product in the database is indeed the one just created
385 VeluxProduct existing = existingProducts.get(new ProductBridgeIndex(PRODUCT_INDEX_B));
386 assertEquals(product, existing);
387 assertTrue(existingProducts.isRegistered(product.getBridgeProductIndex()));
391 * Confirm that the modified products list is functioning.
395 public void testModifiedList() {
396 VeluxExistingProducts existingProducts = getExistingProducts();
397 VeluxProduct[] modified = existingProducts.valuesOfModified();
399 // confirm that the list contains two entries
400 assertEquals(2, modified.length);
402 // confirm the product details for the Somfy product
403 VeluxProduct product = modified[0];
404 assertEquals(STATE_SOMFY, product.getState());
405 assertEquals(MAIN_POSITION_A, product.getCurrentPosition());
406 assertEquals(TARGET_POSITION, product.getTarget());
407 assertEquals(PRODUCT_INDEX_A, product.getBridgeProductIndex().toInt());
408 assertEquals(ACTUATOR_TYPE_SOMFY, product.getActuatorType());
409 assertEquals(VANE_POSITION_A, product.getVanePosition());
410 assertTrue(product.isSomfyProduct());
411 assertNotNull(product.getFunctionalParameters());
413 // confirm the product details for the Velux product
414 product = modified[1];
415 assertEquals(STATE_DONE, product.getState());
416 assertEquals(MAIN_POSITION_B, product.getCurrentPosition());
417 assertEquals(MAIN_POSITION_B, product.getTarget());
418 assertEquals(PRODUCT_INDEX_B, product.getBridgeProductIndex().toInt());
419 assertEquals(ACTUATOR_TYPE_VELUX, product.getActuatorType());
420 assertEquals(UNKNOWN_POSITION, product.getVanePosition());
421 assertNull(product.getFunctionalParameters());
422 assertFalse(product.isSomfyProduct());
424 // reset the dirty flag
425 existingProducts.resetDirtyFlag();
426 assertFalse(existingProducts.isDirty());
428 // confirm modified list is now empty again
429 modified = existingProducts.valuesOfModified();
430 assertEquals(0, modified.length);
434 * Test actuator type setting.
438 public void testActuatorTypeSetting() {
439 VeluxProduct product = new VeluxProduct();
440 assertEquals(ACTUATOR_TYPE_UNDEF, product.getActuatorType());
443 product.setActuatorType(ACTUATOR_TYPE_SOMFY);
444 assertEquals(ACTUATOR_TYPE_SOMFY, product.getActuatorType());
446 // try to set it again
447 product.setActuatorType(ACTUATOR_TYPE_VELUX);
448 assertNotEquals(ACTUATOR_TYPE_VELUX, product.getActuatorType());
450 // try with a clean product
451 product = new VeluxProduct();
452 product.setActuatorType(ACTUATOR_TYPE_VELUX);
453 assertEquals(ACTUATOR_TYPE_VELUX, product.getActuatorType());
457 * Test the SCrunProduct command by creating packet for a given main position and vane position, and checking the
458 * created packet is as expected.
462 public void testSCrunProductA() {
463 final String expectedString = """
464 02 1C 08 05 00 20 00 90 00 00 00 00 00 A0 00 00 00 00 00 00 00 00 00 00 00\
465 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00\
466 00 00 00 00 00 00 00 00 00\
468 final byte[] expectedPacket = toByteArray(expectedString);
469 final int targetMainPosition = 0x9000;
470 final int targetVanePosition = 0xA000;
472 // initialise the product to be commanded
473 VeluxProduct product = new VeluxProduct(VeluxProductName.UNKNOWN, new ProductBridgeIndex(PRODUCT_INDEX_A), 0, 0,
474 0, null, Command.UNDEFTYPE);
475 product.setActuatorType(ACTUATOR_TYPE_SOMFY);
476 product.setCurrentPosition(targetMainPosition);
477 product.setVanePosition(targetVanePosition);
479 // create the run product command, and initialise it from the test product's state values
480 SCrunProductCommand bcp = new SCrunProductCommand();
481 bcp.setNodeIdAndParameters(product.getBridgeProductIndex().toInt(),
482 new VeluxProductPosition(product.getCurrentPosition()), product.getFunctionalParameters());
484 // get the resulting data packet
485 byte[] actualPacket = bcp.getRequestDataAsArrayOfBytes();
487 // check the packet lengths are the same
488 assertEquals(expectedPacket.length, actualPacket.length);
490 // check the packet contents are identical (note start at i = 2 because session id won't match)
491 boolean identical = true;
492 for (int i = 2; i < expectedPacket.length; i++) {
493 if (actualPacket[i] != expectedPacket[i]) {
497 assertTrue(identical);
499 // check the resulting updater product state is 'executing' with the new values
500 product = bcp.getProduct();
501 product.setActuatorType(ACTUATOR_TYPE_SOMFY);
502 assertEquals(ProductState.EXECUTING, product.getProductState());
503 assertEquals(targetMainPosition, product.getCurrentPosition());
504 assertEquals(targetMainPosition, product.getTarget());
505 assertEquals(targetMainPosition, product.getDisplayPosition());
506 assertEquals(targetVanePosition, product.getVanePosition());
507 assertEquals(targetVanePosition, product.getVaneDisplayPosition());
511 * Test the SCrunProduct command by creating a packet with some bad values, and checking the product is as expected.
515 public void testSCrunProductB() {
516 SCrunProductCommand bcp = new SCrunProductCommand();
518 final int errorMainPosition = 0xffff;
519 VeluxProduct product = new VeluxProduct(VeluxProductName.UNKNOWN, new ProductBridgeIndex(PRODUCT_INDEX_A), 0, 0,
520 0, null, Command.UNDEFTYPE);
521 product.setActuatorType(ACTUATOR_TYPE_SOMFY);
522 product.setCurrentPosition(errorMainPosition);
524 bcp.setNodeIdAndParameters(product.getBridgeProductIndex().toInt(),
525 new VeluxProductPosition(product.getCurrentPosition()), product.getFunctionalParameters());
526 product = bcp.getProduct();
527 product.setActuatorType(ACTUATOR_TYPE_SOMFY);
529 assertEquals(ProductState.EXECUTING, product.getProductState());
530 assertEquals(IGNORE_POSITION, product.getCurrentPosition());
531 assertEquals(IGNORE_POSITION, product.getTarget());
532 assertEquals(UNKNOWN_POSITION, product.getDisplayPosition());
533 assertEquals(UNKNOWN_POSITION, product.getVanePosition());
534 assertEquals(UNKNOWN_POSITION, product.getVaneDisplayPosition());
538 * Test the actuator state.
542 public void testActuatorState() {
543 VeluxProduct product = new VeluxProduct(VeluxProductName.UNKNOWN, new ProductBridgeIndex(PRODUCT_INDEX_A), 0, 0,
544 0, null, Command.UNDEFTYPE);
545 int[] inputStates = { 0, 1, 2, 3, 4, 5, 0x2c, 0x2d, 0xff, 0x80 };
546 ProductState[] expected = { ProductState.NON_EXECUTING, ProductState.ERROR, ProductState.NOT_USED,
547 ProductState.WAITING_FOR_POWER, ProductState.EXECUTING, ProductState.DONE, ProductState.EXECUTING,
548 ProductState.DONE, ProductState.UNKNOWN, ProductState.MANUAL };
549 for (int i = 0; i < inputStates.length; i++) {
550 product.setState(inputStates[i]);
551 assertEquals(expected[i], product.getProductState());
556 * Test actuator positions and display positions.
560 public void testActuatorPositions() {
561 VeluxProduct product = new VeluxProduct(VeluxProductName.UNKNOWN, new ProductBridgeIndex(PRODUCT_INDEX_A), 0, 0,
562 0, null, Command.UNDEFTYPE);
564 product.setCurrentPosition(MAIN_POSITION_A);
565 product.setTarget(TARGET_POSITION);
566 product.setActuatorType(ACTUATOR_TYPE_SOMFY);
567 product.setVanePosition(VANE_POSITION_A);
569 // state uninitialised
570 assertEquals(MAIN_POSITION_A, product.getCurrentPosition());
571 assertEquals(MAIN_POSITION_A, product.getDisplayPosition());
572 assertEquals(TARGET_POSITION, product.getTarget());
573 assertEquals(VANE_POSITION_A, product.getVaneDisplayPosition());
574 assertEquals(VANE_POSITION_A, product.getVanePosition());
577 product.setState(ProductState.DONE.value);
578 assertEquals(MAIN_POSITION_A, product.getDisplayPosition());
579 assertEquals(MAIN_POSITION_A, product.getCurrentPosition());
580 assertEquals(TARGET_POSITION, product.getTarget());
581 assertEquals(VANE_POSITION_A, product.getVaneDisplayPosition());
582 assertEquals(VANE_POSITION_A, product.getVanePosition());
585 product.setState(ProductState.NOT_USED.value);
586 assertEquals(MAIN_POSITION_A, product.getDisplayPosition());
587 assertEquals(MAIN_POSITION_A, product.getCurrentPosition());
588 assertEquals(TARGET_POSITION, product.getTarget());
589 assertEquals(VANE_POSITION_A, product.getVaneDisplayPosition());
590 assertEquals(VANE_POSITION_A, product.getVanePosition());
593 product.setState(ProductState.EXECUTING.value);
594 assertEquals(TARGET_POSITION, product.getDisplayPosition());
595 assertEquals(MAIN_POSITION_A, product.getCurrentPosition());
596 assertEquals(TARGET_POSITION, product.getTarget());
597 assertEquals(VANE_POSITION_A, product.getVaneDisplayPosition());
598 assertEquals(VANE_POSITION_A, product.getVanePosition());
600 // state = manual + excuting
601 product.setState(ProductState.MANUAL.value + ProductState.EXECUTING.value);
602 assertEquals(UNKNOWN_POSITION, product.getDisplayPosition());
603 assertEquals(MAIN_POSITION_A, product.getCurrentPosition());
604 assertEquals(TARGET_POSITION, product.getTarget());
605 assertEquals(UNKNOWN_POSITION, product.getVaneDisplayPosition());
606 assertEquals(VANE_POSITION_A, product.getVanePosition());
609 product.setState(ProductState.ERROR.value);
610 assertEquals(UNKNOWN_POSITION, product.getDisplayPosition());
611 assertEquals(MAIN_POSITION_A, product.getCurrentPosition());
612 assertEquals(TARGET_POSITION, product.getTarget());
613 assertEquals(UNKNOWN_POSITION, product.getVaneDisplayPosition());
614 assertEquals(VANE_POSITION_A, product.getVanePosition());
618 * Test the SCgetHouseStatus command by checking for the correct parsing of a 'GW_NODE_STATE_POSITION_CHANGED_NTF'
619 * notification packet. Note: this packet is from a Velux roller shutter with main and vane position.
623 public void testSCgetHouseStatusOnVelux() {
624 // initialise the test parameters
625 final String packet = "00 2D C8 00 B8 00 F7 FF F7 FF 00 00 F7 FF 00 00 4A E5 00 00";
626 final short command = VeluxKLFAPI.Command.GW_NODE_STATE_POSITION_CHANGED_NTF.getShort();
628 // initialise the BCP
629 SCgetHouseStatus bcp = new SCgetHouseStatus();
631 // set the packet response
632 bcp.setResponse(command, toByteArray(packet), false);
635 assertTrue(bcp.isCommunicationSuccessful());
636 assertTrue(bcp.isCommunicationFinished());
638 // initialise the product
639 VeluxProduct product = bcp.getProduct();
641 // change actuator type
642 assertEquals(ACTUATOR_TYPE_UNDEF, product.getActuatorType());
643 product.setActuatorType(ACTUATOR_TYPE_SOMFY);
645 // check negative assertions
646 assertNotEquals(VANE_POSITION_A, product.getVanePosition());
648 // test updating the existing product in the database
649 VeluxExistingProducts existingProducts = getExistingProducts();
651 // process this as a receive only command for a Velux product => update IS applied
652 existingProducts.resetDirtyFlag();
653 assertTrue(existingProducts.update(product));
654 assertTrue(existingProducts.isDirty());
656 // process this as an information request command for a Velux product => update IS applied
657 existingProducts.resetDirtyFlag();
658 product.setCurrentPosition(MAIN_POSITION_B);
659 assertTrue(existingProducts.update(product));
660 assertTrue(existingProducts.isDirty());
664 * Test updating logic with various states applied.
668 public void testUpdatingLogic() {
669 VeluxExistingProducts existingProducts = getExistingProducts();
670 ProductBridgeIndex index = new ProductBridgeIndex(PRODUCT_INDEX_A);
671 VeluxProduct product = existingProducts.get(index).clone();
673 assertEquals(ProductState.DONE, product.getProductState());
676 assertEquals(MAIN_POSITION_A, product.getDisplayPosition());
677 assertEquals(MAIN_POSITION_A, product.getCurrentPosition());
678 assertEquals(TARGET_POSITION, product.getTarget());
679 assertEquals(VANE_POSITION_A, product.getVaneDisplayPosition());
680 assertEquals(VANE_POSITION_A, product.getVanePosition());
683 product.setState(ProductState.NOT_USED.value);
684 product.setCurrentPosition(MAIN_POSITION_A - 1);
685 product.setTarget(TARGET_POSITION - 1);
686 product.setVanePosition(VANE_POSITION_A - 1);
687 existingProducts.update(product);
688 product = existingProducts.get(index).clone();
689 assertEquals(MAIN_POSITION_A, product.getDisplayPosition());
690 assertEquals(MAIN_POSITION_A, product.getCurrentPosition());
691 assertEquals(TARGET_POSITION, product.getTarget());
692 assertEquals(VANE_POSITION_A, product.getVaneDisplayPosition());
693 assertEquals(VANE_POSITION_A, product.getVanePosition());
695 // state = manual + excuting
696 product.setState(ProductState.MANUAL.value + ProductState.EXECUTING.value);
697 product.setCurrentPosition(MAIN_POSITION_A - 1);
698 product.setTarget(TARGET_POSITION - 1);
699 product.setVanePosition(VANE_POSITION_A - 1);
700 existingProducts.update(product);
701 product = existingProducts.get(index).clone();
702 assertEquals(UNKNOWN_POSITION, product.getDisplayPosition());
703 assertEquals(MAIN_POSITION_A, product.getCurrentPosition());
704 assertEquals(TARGET_POSITION, product.getTarget());
705 assertEquals(UNKNOWN_POSITION, product.getVaneDisplayPosition());
706 assertEquals(VANE_POSITION_A, product.getVanePosition());
709 product.setState(ProductState.ERROR.value);
710 product.setCurrentPosition(MAIN_POSITION_A - 1);
711 product.setTarget(TARGET_POSITION - 1);
712 product.setVanePosition(VANE_POSITION_A - 1);
713 existingProducts.update(product);
714 product = existingProducts.get(index).clone();
715 assertEquals(UNKNOWN_POSITION, product.getDisplayPosition());
716 assertEquals(MAIN_POSITION_A, product.getCurrentPosition());
717 assertEquals(TARGET_POSITION, product.getTarget());
718 assertEquals(UNKNOWN_POSITION, product.getVaneDisplayPosition());
719 assertEquals(VANE_POSITION_A, product.getVanePosition());
722 product.setState(ProductState.EXECUTING.value);
723 product.setCurrentPosition(MAIN_POSITION_A - 1);
724 product.setTarget(TARGET_POSITION - 1);
725 product.setVanePosition(VANE_POSITION_A - 1);
726 existingProducts.update(product);
727 product = existingProducts.get(index).clone();
728 assertNotEquals(TARGET_POSITION, product.getDisplayPosition());
729 assertNotEquals(MAIN_POSITION_A, product.getCurrentPosition());
730 assertNotEquals(TARGET_POSITION, product.getTarget());
731 assertNotEquals(VANE_POSITION_A, product.getVaneDisplayPosition());
732 assertNotEquals(VANE_POSITION_A, product.getVanePosition());
735 product.setState(ProductState.EXECUTING.value);
736 product.setCurrentPosition(MAIN_POSITION_A);
737 product.setTarget(TARGET_POSITION);
738 product.setVanePosition(VANE_POSITION_A);
739 existingProducts.update(product);
740 product = existingProducts.get(index).clone();
741 assertEquals(TARGET_POSITION, product.getDisplayPosition());
742 assertEquals(MAIN_POSITION_A, product.getCurrentPosition());
743 assertEquals(TARGET_POSITION, product.getTarget());
744 assertEquals(VANE_POSITION_A, product.getVaneDisplayPosition());
745 assertEquals(VANE_POSITION_A, product.getVanePosition());
749 * Test updating the existing product in the database with special exceptions.
753 public void testSpecialExceptions() {
754 VeluxExistingProducts existingProducts = getExistingProducts();
755 VeluxProduct existing = existingProducts.get(new ProductBridgeIndex(PRODUCT_INDEX_A));
757 VeluxProduct product;
759 // process this as a receive only command for a Somfy product => update IS applied
760 product = new VeluxProduct(existing.getProductName(), existing.getBridgeProductIndex(), existing.getState(),
761 existing.getCurrentPosition(), existing.getTarget(), existing.getFunctionalParameters(),
762 Command.GW_OPENHAB_RECEIVEONLY);
763 existingProducts.resetDirtyFlag();
764 product.setState(ProductState.DONE.value);
765 product.setCurrentPosition(MAIN_POSITION_B);
766 assertTrue(existingProducts.update(product));
767 assertTrue(existingProducts.isDirty());
769 // process this as an information request command for a Somfy product => update IS applied
770 product = new VeluxProduct(existing.getProductName(), existing.getBridgeProductIndex(), existing.getState(),
771 existing.getCurrentPosition(), existing.getTarget(), existing.getFunctionalParameters(),
772 Command.GW_GET_NODE_INFORMATION_REQ);
773 existingProducts.resetDirtyFlag();
774 product.setCurrentPosition(MAIN_POSITION_A);
775 assertTrue(existingProducts.update(product));
776 assertTrue(existingProducts.isDirty());
778 // process this as a receive only command for a Somfy product with bad data => update NOT applied
779 product = new VeluxProduct(existing.getProductName(), existing.getBridgeProductIndex(), existing.getState(),
780 existing.getCurrentPosition(), existing.getTarget(), existing.getFunctionalParameters(),
781 Command.GW_OPENHAB_RECEIVEONLY);
782 existingProducts.resetDirtyFlag();
783 product.setCurrentPosition(UNKNOWN_POSITION);
784 product.setTarget(UNKNOWN_POSITION);
785 assertTrue(existingProducts.update(product));
786 assertFalse(existingProducts.isDirty());
790 * Test VeluxProductPosition
794 public void testVeluxProductPosition() {
795 VeluxProductPosition position;
798 // on and inside range limits
799 assertTrue(new VeluxProductPosition(VeluxProductPosition.VPP_VELUX_MIN).isValid());
800 assertTrue(new VeluxProductPosition(VeluxProductPosition.VPP_VELUX_MAX).isValid());
801 assertTrue(new VeluxProductPosition(VeluxProductPosition.VPP_VELUX_MAX - 1).isValid());
802 assertTrue(new VeluxProductPosition(VeluxProductPosition.VPP_VELUX_MIN + 1).isValid());
804 // outside range limits
805 assertFalse(new VeluxProductPosition(VeluxProductPosition.VPP_VELUX_MIN - 1).isValid());
806 assertFalse(new VeluxProductPosition(VeluxProductPosition.VPP_VELUX_MAX + 1).isValid());
807 assertFalse(new VeluxProductPosition(VeluxProductPosition.VPP_VELUX_IGNORE).isValid());
808 assertFalse(new VeluxProductPosition(VeluxProductPosition.VPP_VELUX_DEFAULT).isValid());
809 assertFalse(new VeluxProductPosition(VeluxProductPosition.VPP_VELUX_STOP).isValid());
810 assertFalse(new VeluxProductPosition(VeluxProductPosition.VPP_VELUX_UNKNOWN).isValid());
812 // 80% absolute position
813 position = new VeluxProductPosition(new PercentType(80));
814 assertEquals(0xA000, position.getPositionAsVeluxType());
815 assertTrue(position.isValid());
817 // 80% absolute position
818 position = new VeluxProductPosition(new PercentType(80), false);
819 assertEquals(0xA000, position.getPositionAsVeluxType());
820 assertTrue(position.isValid());
822 // 80% inverted absolute position (i.e. 20%)
823 position = new VeluxProductPosition(new PercentType(80), true);
824 assertEquals(0x2800, position.getPositionAsVeluxType());
825 assertTrue(position.isValid());
827 // 80% positive relative position
828 target = VeluxProductPosition.VPP_VELUX_RELATIVE_ORIGIN
829 + (VeluxProductPosition.VPP_VELUX_RELATIVE_RANGE * 8 / 10);
830 position = new VeluxProductPosition(new PercentType(80)).overridePositionType(PositionType.OFFSET_POSITIVE);
831 assertTrue(position.isValid());
832 assertEquals(target, position.getPositionAsVeluxType());
834 // 80% negative relative position
835 target = VeluxProductPosition.VPP_VELUX_RELATIVE_ORIGIN
836 - (VeluxProductPosition.VPP_VELUX_RELATIVE_RANGE * 8 / 10);
837 position = new VeluxProductPosition(new PercentType(80)).overridePositionType(PositionType.OFFSET_NEGATIVE);
838 assertTrue(position.isValid());
839 assertEquals(target, position.getPositionAsVeluxType());
843 * Test SCrunProductResult results
847 public void testSCrunProductResults() {
848 SCrunProductCommand bcp = new SCrunProductCommand();
850 // create a dummy product to get some functional parameters from
851 VeluxProduct product = new VeluxProduct(VeluxProductName.UNKNOWN, new ProductBridgeIndex(PRODUCT_INDEX_A), 0, 0,
852 0, null, Command.UNDEFTYPE);
853 product.setActuatorType(ACTUATOR_TYPE_SOMFY);
854 product.setVanePosition(VANE_POSITION_A);
855 final FunctionalParameters functionalParameters = product.getFunctionalParameters();
859 // test setting both main and vane position
860 ok = bcp.setNodeIdAndParameters(PRODUCT_INDEX_A, new VeluxProductPosition(MAIN_POSITION_A),
861 functionalParameters);
863 product = bcp.getProduct();
864 product.setActuatorType(ACTUATOR_TYPE_SOMFY);
865 assertEquals(MAIN_POSITION_A, product.getCurrentPosition());
866 assertEquals(VANE_POSITION_A, product.getVanePosition());
868 // test setting vane position only
869 ok = bcp.setNodeIdAndParameters(PRODUCT_INDEX_A, null, functionalParameters);
871 product = bcp.getProduct();
872 product.setActuatorType(ACTUATOR_TYPE_SOMFY);
873 assertEquals(IGNORE_POSITION, product.getCurrentPosition());
874 assertEquals(VANE_POSITION_A, product.getVanePosition());
876 // test setting main position only
877 ok = bcp.setNodeIdAndParameters(PRODUCT_INDEX_A, new VeluxProductPosition(MAIN_POSITION_A), null);
879 product = bcp.getProduct();
880 product.setActuatorType(ACTUATOR_TYPE_SOMFY);
881 assertEquals(MAIN_POSITION_A, product.getCurrentPosition());
882 assertEquals(UNKNOWN_POSITION, product.getVanePosition());
884 // test setting neither
885 ok = bcp.setNodeIdAndParameters(PRODUCT_INDEX_A, null, null);
890 * Test SCgetProductStatus exceptional error state processing.
894 public void testErrorStateMapping() {
895 // initialise the test parameters
896 final String packet = """
897 0F A3 01 06 01 00 01 02 00 9A 36 03 00 00 00 00 00 00 00 00 00 00 00 00 00\
898 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\
901 final Command command = VeluxKLFAPI.Command.GW_STATUS_REQUEST_NTF;
903 // initialise the BCP
904 SCgetProductStatus bcp = new SCgetProductStatus();
905 bcp.setProductId(PRODUCT_INDEX_A);
907 // set the packet response
908 bcp.setResponse(command.getShort(), toByteArray(packet), false);
911 assertTrue(bcp.isCommunicationSuccessful());
912 assertTrue(bcp.isCommunicationFinished());
914 // check the product state
915 assertEquals(ProductState.UNKNOWN.value, bcp.getProduct().getState());