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 = "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"
111 + " 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"
112 + " 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"
113 + " 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";
114 final Command command = VeluxKLFAPI.Command.GW_GET_NODE_INFORMATION_NTF;
116 // initialise the BCP
117 SCgetProduct bcp = new SCgetProduct();
118 bcp.setProductId(PRODUCT_INDEX_A);
120 // set the packet response
121 bcp.setResponse(command.getShort(), toByteArray(packet), false);
124 assertTrue(bcp.isCommunicationSuccessful());
125 assertTrue(bcp.isCommunicationFinished());
127 // initialise the product
128 VeluxProduct product = bcp.getProduct();
130 // check positive assertions
131 assertEquals(STATE_SOMFY, product.getState());
132 assertEquals(MAIN_POSITION_A, product.getCurrentPosition());
133 assertEquals(MAIN_POSITION_A, product.getTarget());
134 assertEquals(PRODUCT_INDEX_A, product.getBridgeProductIndex().toInt());
135 assertEquals(ACTUATOR_TYPE_SOMFY, product.getActuatorType());
136 assertEquals(UNKNOWN_POSITION, product.getVanePosition());
137 assertNull(product.getFunctionalParameters());
138 assertTrue(product.supportsVanePosition());
139 assertTrue(product.isSomfyProduct());
140 assertEquals(ProductState.DONE, product.getProductState());
142 // check negative assertions
143 assertNotEquals(VANE_POSITION_A, product.getVanePosition());
145 // register in existing products database
146 VeluxExistingProducts existingProducts = getExistingProducts();
147 assertTrue(existingProducts.register(product));
148 assertTrue(existingProducts.isRegistered(product));
149 assertTrue(existingProducts.isRegistered(product.getBridgeProductIndex()));
150 assertEquals(1, existingProducts.getNoMembers());
152 // confirm that a dummy product is NOT in the database
153 assertFalse(existingProducts.isRegistered(new ProductBridgeIndex(99)));
156 assertTrue(existingProducts.isDirty());
157 existingProducts.resetDirtyFlag();
158 assertFalse(existingProducts.isDirty());
160 // re-registering the same product should return false
161 assertFalse(existingProducts.register(product));
163 // updating again with the same data should NOT set the dirty flag
164 assertTrue(existingProducts.update(product));
165 assertFalse(existingProducts.isDirty());
167 // check that the product in the database is indeed the one just created
168 VeluxProduct existing = existingProducts.get(new ProductBridgeIndex(PRODUCT_INDEX_A));
169 assertEquals(product, existing);
170 assertEquals(1, existingProducts.getNoMembers());
171 assertTrue(existingProducts.isRegistered(product.getBridgeProductIndex()));
175 * Confirm that the product in the existing database has the same values as the product created and added in test 3.
179 public void testExistingUnknownVanePosition() {
180 VeluxExistingProducts existingProducts = getExistingProducts();
181 VeluxProduct product = existingProducts.get(new ProductBridgeIndex(PRODUCT_INDEX_A));
183 // confirm the product details
184 assertEquals(STATE_SOMFY, product.getState());
185 assertEquals(MAIN_POSITION_A, product.getCurrentPosition());
186 assertEquals(MAIN_POSITION_A, product.getTarget());
187 assertEquals(PRODUCT_INDEX_A, product.getBridgeProductIndex().toInt());
188 assertEquals(ACTUATOR_TYPE_SOMFY, product.getActuatorType());
189 assertEquals(UNKNOWN_POSITION, product.getVanePosition());
190 assertNull(product.getFunctionalParameters());
194 * Test the SCgetProductStatus command by checking for the correct parsing of a 'GW_STATUS_REQUEST_NTF' notification
195 * packet. Note: this packet is from a Somfy roller shutter with main and vane position.
199 public void testSCgetProductStatus() {
200 // initialise the test parameters
201 final String packet = "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"
202 + " 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";
203 final Command command = VeluxKLFAPI.Command.GW_STATUS_REQUEST_NTF;
205 // initialise the BCP
206 SCgetProductStatus bcp = new SCgetProductStatus();
207 bcp.setProductId(PRODUCT_INDEX_A);
209 // set the packet response
210 bcp.setResponse(command.getShort(), toByteArray(packet), false);
213 assertTrue(bcp.isCommunicationSuccessful());
214 assertTrue(bcp.isCommunicationFinished());
216 // initialise the product
217 VeluxProduct product = bcp.getProduct();
219 // change actuator type
220 assertEquals(ACTUATOR_TYPE_UNDEF, product.getActuatorType());
221 product.setActuatorType(ACTUATOR_TYPE_SOMFY);
223 // check positive assertions
224 assertEquals(STATE_DONE, product.getState());
225 assertEquals(MAIN_POSITION_A, product.getCurrentPosition());
226 assertEquals(IGNORE_POSITION, product.getTarget());
227 assertEquals(PRODUCT_INDEX_A, product.getBridgeProductIndex().toInt());
228 assertEquals(ACTUATOR_TYPE_SOMFY, product.getActuatorType());
229 assertEquals(VANE_POSITION_A, product.getVanePosition());
230 assertNotNull(product.getFunctionalParameters());
231 assertEquals(ProductState.DONE, product.getProductState());
233 // test updating the existing product in the database
234 VeluxExistingProducts existingProducts = getExistingProducts();
235 assertTrue(existingProducts.update(product));
236 assertTrue(existingProducts.isDirty());
238 // updating again with the same data should NOT set the dirty flag
239 existingProducts.resetDirtyFlag();
240 assertTrue(existingProducts.update(product));
241 assertFalse(existingProducts.isDirty());
245 * Confirm that the product in the existing database has the same values as the product created and updated to the
246 * database in test 5.
250 public void testExistingValidVanePosition() {
251 VeluxExistingProducts existingProducts = getExistingProducts();
252 VeluxProduct product = existingProducts.get(new ProductBridgeIndex(PRODUCT_INDEX_A));
254 // confirm the product details
255 assertEquals(STATE_DONE, product.getState());
256 assertEquals(MAIN_POSITION_A, product.getCurrentPosition());
257 assertEquals(MAIN_POSITION_A, product.getTarget());
258 assertEquals(PRODUCT_INDEX_A, product.getBridgeProductIndex().toInt());
259 assertEquals(ACTUATOR_TYPE_SOMFY, product.getActuatorType());
260 assertEquals(VANE_POSITION_A, product.getVanePosition());
261 assertNotNull(product.getFunctionalParameters());
262 assertEquals(ProductState.DONE, product.getProductState());
266 * Test the SCgetHouseStatus command by checking for the correct parsing of a 'GW_NODE_STATE_POSITION_CHANGED_NTF'
267 * notification packet. Note: this packet is from a Somfy roller shutter with main and vane position.
271 public void testSCgetHouseStatus() {
272 // initialise the test parameters
273 final String packet = "06 2D C8 00 B8 00 F7 FF F7 FF 00 00 F7 FF 00 00 4A E5 00 00";
274 final short command = VeluxKLFAPI.Command.GW_NODE_STATE_POSITION_CHANGED_NTF.getShort();
276 // initialise the BCP
277 SCgetHouseStatus bcp = new SCgetHouseStatus();
279 // set the packet response
280 bcp.setResponse(command, toByteArray(packet), false);
283 assertTrue(bcp.isCommunicationSuccessful());
284 assertTrue(bcp.isCommunicationFinished());
286 // initialise the product
287 VeluxProduct product = bcp.getProduct();
289 // change actuator type
290 assertEquals(ACTUATOR_TYPE_UNDEF, product.getActuatorType());
291 product.setActuatorType(ACTUATOR_TYPE_SOMFY);
293 // check positive assertions
294 assertEquals(STATE_SOMFY, product.getState());
295 assertEquals(MAIN_POSITION_A, product.getCurrentPosition());
296 assertEquals(TARGET_POSITION, product.getTarget());
297 assertEquals(PRODUCT_INDEX_A, product.getBridgeProductIndex().toInt());
298 assertEquals(ACTUATOR_TYPE_SOMFY, product.getActuatorType());
299 assertEquals(UNKNOWN_POSITION, product.getVanePosition());
300 assertNull(product.getFunctionalParameters());
302 // check negative assertions
303 assertNotEquals(VANE_POSITION_A, product.getVanePosition());
305 VeluxExistingProducts existingProducts = getExistingProducts();
306 existingProducts.update(product);
310 * Confirm that the product in the existing database has the same values as the product created and updated to the
311 * database in test 7.
315 public void testExistingValidVanePositionWithNewTargetValue() {
316 VeluxExistingProducts existingProducts = getExistingProducts();
317 VeluxProduct product = existingProducts.get(new ProductBridgeIndex(PRODUCT_INDEX_A));
319 // confirm the product details
320 assertEquals(STATE_SOMFY, product.getState());
321 assertEquals(MAIN_POSITION_A, product.getCurrentPosition());
322 assertEquals(TARGET_POSITION, product.getTarget());
323 assertEquals(PRODUCT_INDEX_A, product.getBridgeProductIndex().toInt());
324 assertEquals(ACTUATOR_TYPE_SOMFY, product.getActuatorType());
325 assertEquals(VANE_POSITION_A, product.getVanePosition());
326 assertNotNull(product.getFunctionalParameters());
330 * Test the SCgetProduct by checking for the correct parsing of a 'GW_GET_NODE_INFORMATION_NTF' notification packet.
331 * Note: this packet is from a Velux roof window without vane position.
335 public void testSCgetProductOnVelux() {
336 // initialise the test parameters
337 final String packet = "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"
338 + " 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"
339 + " 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"
340 + " 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";
341 final short command = VeluxKLFAPI.Command.GW_GET_NODE_INFORMATION_NTF.getShort();
343 // initialise the BCP
344 SCgetProduct bcp = new SCgetProduct();
345 bcp.setProductId(PRODUCT_INDEX_B);
347 // set the packet response
348 bcp.setResponse(command, toByteArray(packet), false);
351 assertTrue(bcp.isCommunicationSuccessful());
352 assertTrue(bcp.isCommunicationFinished());
354 // initialise the product
355 VeluxProduct product = bcp.getProduct();
357 // check positive assertions
358 assertEquals(STATE_DONE, product.getState());
359 assertEquals(MAIN_POSITION_B, product.getCurrentPosition());
360 assertEquals(MAIN_POSITION_B, product.getTarget());
361 assertEquals(PRODUCT_INDEX_B, product.getBridgeProductIndex().toInt());
362 assertEquals(ACTUATOR_TYPE_VELUX, product.getActuatorType());
363 assertEquals(UNKNOWN_POSITION, product.getVanePosition());
364 assertNull(product.getFunctionalParameters());
365 assertFalse(product.isSomfyProduct());
367 // check negative assertions
368 assertFalse(product.supportsVanePosition());
369 assertNotEquals(VANE_POSITION_A, product.getVanePosition());
371 // register in existing products database
372 VeluxExistingProducts existingProducts = getExistingProducts();
373 assertTrue(existingProducts.register(product));
374 assertTrue(existingProducts.isRegistered(product));
375 assertTrue(existingProducts.isRegistered(product.getBridgeProductIndex()));
376 assertEquals(2, existingProducts.getNoMembers());
378 // check that the product in the database is indeed the one just created
379 VeluxProduct existing = existingProducts.get(new ProductBridgeIndex(PRODUCT_INDEX_B));
380 assertEquals(product, existing);
381 assertTrue(existingProducts.isRegistered(product.getBridgeProductIndex()));
385 * Confirm that the modified products list is functioning.
389 public void testModifiedList() {
390 VeluxExistingProducts existingProducts = getExistingProducts();
391 VeluxProduct[] modified = existingProducts.valuesOfModified();
393 // confirm that the list contains two entries
394 assertEquals(2, modified.length);
396 // confirm the product details for the Somfy product
397 VeluxProduct product = modified[0];
398 assertEquals(STATE_SOMFY, product.getState());
399 assertEquals(MAIN_POSITION_A, product.getCurrentPosition());
400 assertEquals(TARGET_POSITION, product.getTarget());
401 assertEquals(PRODUCT_INDEX_A, product.getBridgeProductIndex().toInt());
402 assertEquals(ACTUATOR_TYPE_SOMFY, product.getActuatorType());
403 assertEquals(VANE_POSITION_A, product.getVanePosition());
404 assertTrue(product.isSomfyProduct());
405 assertNotNull(product.getFunctionalParameters());
407 // confirm the product details for the Velux product
408 product = modified[1];
409 assertEquals(STATE_DONE, product.getState());
410 assertEquals(MAIN_POSITION_B, product.getCurrentPosition());
411 assertEquals(MAIN_POSITION_B, product.getTarget());
412 assertEquals(PRODUCT_INDEX_B, product.getBridgeProductIndex().toInt());
413 assertEquals(ACTUATOR_TYPE_VELUX, product.getActuatorType());
414 assertEquals(UNKNOWN_POSITION, product.getVanePosition());
415 assertNull(product.getFunctionalParameters());
416 assertFalse(product.isSomfyProduct());
418 // reset the dirty flag
419 existingProducts.resetDirtyFlag();
420 assertFalse(existingProducts.isDirty());
422 // confirm modified list is now empty again
423 modified = existingProducts.valuesOfModified();
424 assertEquals(0, modified.length);
428 * Test actuator type setting.
432 public void testActuatorTypeSetting() {
433 VeluxProduct product = new VeluxProduct();
434 assertEquals(ACTUATOR_TYPE_UNDEF, product.getActuatorType());
437 product.setActuatorType(ACTUATOR_TYPE_SOMFY);
438 assertEquals(ACTUATOR_TYPE_SOMFY, product.getActuatorType());
440 // try to set it again
441 product.setActuatorType(ACTUATOR_TYPE_VELUX);
442 assertNotEquals(ACTUATOR_TYPE_VELUX, product.getActuatorType());
444 // try with a clean product
445 product = new VeluxProduct();
446 product.setActuatorType(ACTUATOR_TYPE_VELUX);
447 assertEquals(ACTUATOR_TYPE_VELUX, product.getActuatorType());
451 * Test the SCrunProduct command by creating packet for a given main position and vane position, and checking the
452 * created packet is as expected.
456 public void testSCrunProductA() {
457 final String expectedString = "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"
458 + " 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"
459 + " 00 00 00 00 00 00 00 00 00";
460 final byte[] expectedPacket = toByteArray(expectedString);
461 final int targetMainPosition = 0x9000;
462 final int targetVanePosition = 0xA000;
464 // initialise the product to be commanded
465 VeluxProduct product = new VeluxProduct(VeluxProductName.UNKNOWN, new ProductBridgeIndex(PRODUCT_INDEX_A), 0, 0,
466 0, null, Command.UNDEFTYPE);
467 product.setActuatorType(ACTUATOR_TYPE_SOMFY);
468 product.setCurrentPosition(targetMainPosition);
469 product.setVanePosition(targetVanePosition);
471 // create the run product command, and initialise it from the test product's state values
472 SCrunProductCommand bcp = new SCrunProductCommand();
473 bcp.setNodeIdAndParameters(product.getBridgeProductIndex().toInt(),
474 new VeluxProductPosition(product.getCurrentPosition()), product.getFunctionalParameters());
476 // get the resulting data packet
477 byte[] actualPacket = bcp.getRequestDataAsArrayOfBytes();
479 // check the packet lengths are the same
480 assertEquals(expectedPacket.length, actualPacket.length);
482 // check the packet contents are identical (note start at i = 2 because session id won't match)
483 boolean identical = true;
484 for (int i = 2; i < expectedPacket.length; i++) {
485 if (actualPacket[i] != expectedPacket[i]) {
489 assertTrue(identical);
491 // check the resulting updater product state is 'executing' with the new values
492 product = bcp.getProduct();
493 product.setActuatorType(ACTUATOR_TYPE_SOMFY);
494 assertEquals(ProductState.EXECUTING, product.getProductState());
495 assertEquals(targetMainPosition, product.getCurrentPosition());
496 assertEquals(targetMainPosition, product.getTarget());
497 assertEquals(targetMainPosition, product.getDisplayPosition());
498 assertEquals(targetVanePosition, product.getVanePosition());
499 assertEquals(targetVanePosition, product.getVaneDisplayPosition());
503 * Test the SCrunProduct command by creating a packet with some bad values, and checking the product is as expected.
507 public void testSCrunProductB() {
508 SCrunProductCommand bcp = new SCrunProductCommand();
510 final int errorMainPosition = 0xffff;
511 VeluxProduct product = new VeluxProduct(VeluxProductName.UNKNOWN, new ProductBridgeIndex(PRODUCT_INDEX_A), 0, 0,
512 0, null, Command.UNDEFTYPE);
513 product.setActuatorType(ACTUATOR_TYPE_SOMFY);
514 product.setCurrentPosition(errorMainPosition);
516 bcp.setNodeIdAndParameters(product.getBridgeProductIndex().toInt(),
517 new VeluxProductPosition(product.getCurrentPosition()), product.getFunctionalParameters());
518 product = bcp.getProduct();
519 product.setActuatorType(ACTUATOR_TYPE_SOMFY);
521 assertEquals(ProductState.EXECUTING, product.getProductState());
522 assertEquals(IGNORE_POSITION, product.getCurrentPosition());
523 assertEquals(IGNORE_POSITION, product.getTarget());
524 assertEquals(UNKNOWN_POSITION, product.getDisplayPosition());
525 assertEquals(UNKNOWN_POSITION, product.getVanePosition());
526 assertEquals(UNKNOWN_POSITION, product.getVaneDisplayPosition());
530 * Test the actuator state.
534 public void testActuatorState() {
535 VeluxProduct product = new VeluxProduct(VeluxProductName.UNKNOWN, new ProductBridgeIndex(PRODUCT_INDEX_A), 0, 0,
536 0, null, Command.UNDEFTYPE);
537 int[] inputStates = { 0, 1, 2, 3, 4, 5, 0x2c, 0x2d, 0xff, 0x80 };
538 ProductState[] expected = { ProductState.NON_EXECUTING, ProductState.ERROR, ProductState.NOT_USED,
539 ProductState.WAITING_FOR_POWER, ProductState.EXECUTING, ProductState.DONE, ProductState.EXECUTING,
540 ProductState.DONE, ProductState.UNKNOWN, ProductState.MANUAL };
541 for (int i = 0; i < inputStates.length; i++) {
542 product.setState(inputStates[i]);
543 assertEquals(expected[i], product.getProductState());
548 * Test actuator positions and display positions.
552 public void testActuatorPositions() {
553 VeluxProduct product = new VeluxProduct(VeluxProductName.UNKNOWN, new ProductBridgeIndex(PRODUCT_INDEX_A), 0, 0,
554 0, null, Command.UNDEFTYPE);
556 product.setCurrentPosition(MAIN_POSITION_A);
557 product.setTarget(TARGET_POSITION);
558 product.setActuatorType(ACTUATOR_TYPE_SOMFY);
559 product.setVanePosition(VANE_POSITION_A);
561 // state uninitialised
562 assertEquals(MAIN_POSITION_A, product.getCurrentPosition());
563 assertEquals(MAIN_POSITION_A, product.getDisplayPosition());
564 assertEquals(TARGET_POSITION, product.getTarget());
565 assertEquals(VANE_POSITION_A, product.getVaneDisplayPosition());
566 assertEquals(VANE_POSITION_A, product.getVanePosition());
569 product.setState(ProductState.DONE.value);
570 assertEquals(MAIN_POSITION_A, product.getDisplayPosition());
571 assertEquals(MAIN_POSITION_A, product.getCurrentPosition());
572 assertEquals(TARGET_POSITION, product.getTarget());
573 assertEquals(VANE_POSITION_A, product.getVaneDisplayPosition());
574 assertEquals(VANE_POSITION_A, product.getVanePosition());
577 product.setState(ProductState.NOT_USED.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.EXECUTING.value);
586 assertEquals(TARGET_POSITION, 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());
592 // state = manual + excuting
593 product.setState(ProductState.MANUAL.value + ProductState.EXECUTING.value);
594 assertEquals(UNKNOWN_POSITION, product.getDisplayPosition());
595 assertEquals(MAIN_POSITION_A, product.getCurrentPosition());
596 assertEquals(TARGET_POSITION, product.getTarget());
597 assertEquals(UNKNOWN_POSITION, product.getVaneDisplayPosition());
598 assertEquals(VANE_POSITION_A, product.getVanePosition());
601 product.setState(ProductState.ERROR.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());
610 * Test the SCgetHouseStatus command by checking for the correct parsing of a 'GW_NODE_STATE_POSITION_CHANGED_NTF'
611 * notification packet. Note: this packet is from a Velux roller shutter with main and vane position.
615 public void testSCgetHouseStatusOnVelux() {
616 // initialise the test parameters
617 final String packet = "00 2D C8 00 B8 00 F7 FF F7 FF 00 00 F7 FF 00 00 4A E5 00 00";
618 final short command = VeluxKLFAPI.Command.GW_NODE_STATE_POSITION_CHANGED_NTF.getShort();
620 // initialise the BCP
621 SCgetHouseStatus bcp = new SCgetHouseStatus();
623 // set the packet response
624 bcp.setResponse(command, toByteArray(packet), false);
627 assertTrue(bcp.isCommunicationSuccessful());
628 assertTrue(bcp.isCommunicationFinished());
630 // initialise the product
631 VeluxProduct product = bcp.getProduct();
633 // change actuator type
634 assertEquals(ACTUATOR_TYPE_UNDEF, product.getActuatorType());
635 product.setActuatorType(ACTUATOR_TYPE_SOMFY);
637 // check negative assertions
638 assertNotEquals(VANE_POSITION_A, product.getVanePosition());
640 // test updating the existing product in the database
641 VeluxExistingProducts existingProducts = getExistingProducts();
643 // process this as a receive only command for a Velux product => update IS applied
644 existingProducts.resetDirtyFlag();
645 assertTrue(existingProducts.update(product));
646 assertTrue(existingProducts.isDirty());
648 // process this as an information request command for a Velux product => update IS applied
649 existingProducts.resetDirtyFlag();
650 product.setCurrentPosition(MAIN_POSITION_B);
651 assertTrue(existingProducts.update(product));
652 assertTrue(existingProducts.isDirty());
656 * Test updating logic with various states applied.
660 public void testUpdatingLogic() {
661 VeluxExistingProducts existingProducts = getExistingProducts();
662 ProductBridgeIndex index = new ProductBridgeIndex(PRODUCT_INDEX_A);
663 VeluxProduct product = existingProducts.get(index).clone();
665 assertEquals(ProductState.DONE, product.getProductState());
668 assertEquals(MAIN_POSITION_A, product.getDisplayPosition());
669 assertEquals(MAIN_POSITION_A, product.getCurrentPosition());
670 assertEquals(TARGET_POSITION, product.getTarget());
671 assertEquals(VANE_POSITION_A, product.getVaneDisplayPosition());
672 assertEquals(VANE_POSITION_A, product.getVanePosition());
675 product.setState(ProductState.NOT_USED.value);
676 product.setCurrentPosition(MAIN_POSITION_A - 1);
677 product.setTarget(TARGET_POSITION - 1);
678 product.setVanePosition(VANE_POSITION_A - 1);
679 existingProducts.update(product);
680 product = existingProducts.get(index).clone();
681 assertEquals(MAIN_POSITION_A, product.getDisplayPosition());
682 assertEquals(MAIN_POSITION_A, product.getCurrentPosition());
683 assertEquals(TARGET_POSITION, product.getTarget());
684 assertEquals(VANE_POSITION_A, product.getVaneDisplayPosition());
685 assertEquals(VANE_POSITION_A, product.getVanePosition());
687 // state = manual + excuting
688 product.setState(ProductState.MANUAL.value + ProductState.EXECUTING.value);
689 product.setCurrentPosition(MAIN_POSITION_A - 1);
690 product.setTarget(TARGET_POSITION - 1);
691 product.setVanePosition(VANE_POSITION_A - 1);
692 existingProducts.update(product);
693 product = existingProducts.get(index).clone();
694 assertEquals(UNKNOWN_POSITION, product.getDisplayPosition());
695 assertEquals(MAIN_POSITION_A, product.getCurrentPosition());
696 assertEquals(TARGET_POSITION, product.getTarget());
697 assertEquals(UNKNOWN_POSITION, product.getVaneDisplayPosition());
698 assertEquals(VANE_POSITION_A, product.getVanePosition());
701 product.setState(ProductState.ERROR.value);
702 product.setCurrentPosition(MAIN_POSITION_A - 1);
703 product.setTarget(TARGET_POSITION - 1);
704 product.setVanePosition(VANE_POSITION_A - 1);
705 existingProducts.update(product);
706 product = existingProducts.get(index).clone();
707 assertEquals(UNKNOWN_POSITION, product.getDisplayPosition());
708 assertEquals(MAIN_POSITION_A, product.getCurrentPosition());
709 assertEquals(TARGET_POSITION, product.getTarget());
710 assertEquals(UNKNOWN_POSITION, product.getVaneDisplayPosition());
711 assertEquals(VANE_POSITION_A, product.getVanePosition());
714 product.setState(ProductState.EXECUTING.value);
715 product.setCurrentPosition(MAIN_POSITION_A - 1);
716 product.setTarget(TARGET_POSITION - 1);
717 product.setVanePosition(VANE_POSITION_A - 1);
718 existingProducts.update(product);
719 product = existingProducts.get(index).clone();
720 assertNotEquals(TARGET_POSITION, product.getDisplayPosition());
721 assertNotEquals(MAIN_POSITION_A, product.getCurrentPosition());
722 assertNotEquals(TARGET_POSITION, product.getTarget());
723 assertNotEquals(VANE_POSITION_A, product.getVaneDisplayPosition());
724 assertNotEquals(VANE_POSITION_A, product.getVanePosition());
727 product.setState(ProductState.EXECUTING.value);
728 product.setCurrentPosition(MAIN_POSITION_A);
729 product.setTarget(TARGET_POSITION);
730 product.setVanePosition(VANE_POSITION_A);
731 existingProducts.update(product);
732 product = existingProducts.get(index).clone();
733 assertEquals(TARGET_POSITION, product.getDisplayPosition());
734 assertEquals(MAIN_POSITION_A, product.getCurrentPosition());
735 assertEquals(TARGET_POSITION, product.getTarget());
736 assertEquals(VANE_POSITION_A, product.getVaneDisplayPosition());
737 assertEquals(VANE_POSITION_A, product.getVanePosition());
741 * Test updating the existing product in the database with special exceptions.
745 public void testSpecialExceptions() {
746 VeluxExistingProducts existingProducts = getExistingProducts();
747 VeluxProduct existing = existingProducts.get(new ProductBridgeIndex(PRODUCT_INDEX_A));
749 VeluxProduct product;
751 // process this as a receive only command for a Somfy product => update IS applied
752 product = new VeluxProduct(existing.getProductName(), existing.getBridgeProductIndex(), existing.getState(),
753 existing.getCurrentPosition(), existing.getTarget(), existing.getFunctionalParameters(),
754 Command.GW_OPENHAB_RECEIVEONLY);
755 existingProducts.resetDirtyFlag();
756 product.setState(ProductState.DONE.value);
757 product.setCurrentPosition(MAIN_POSITION_B);
758 assertTrue(existingProducts.update(product));
759 assertTrue(existingProducts.isDirty());
761 // process this as an information request command for a Somfy product => update IS applied
762 product = new VeluxProduct(existing.getProductName(), existing.getBridgeProductIndex(), existing.getState(),
763 existing.getCurrentPosition(), existing.getTarget(), existing.getFunctionalParameters(),
764 Command.GW_GET_NODE_INFORMATION_REQ);
765 existingProducts.resetDirtyFlag();
766 product.setCurrentPosition(MAIN_POSITION_A);
767 assertTrue(existingProducts.update(product));
768 assertTrue(existingProducts.isDirty());
770 // process this as a receive only command for a Somfy product with bad data => update NOT applied
771 product = new VeluxProduct(existing.getProductName(), existing.getBridgeProductIndex(), existing.getState(),
772 existing.getCurrentPosition(), existing.getTarget(), existing.getFunctionalParameters(),
773 Command.GW_OPENHAB_RECEIVEONLY);
774 existingProducts.resetDirtyFlag();
775 product.setCurrentPosition(UNKNOWN_POSITION);
776 product.setTarget(UNKNOWN_POSITION);
777 assertTrue(existingProducts.update(product));
778 assertFalse(existingProducts.isDirty());
782 * Test VeluxProductPosition
786 public void testVeluxProductPosition() {
787 VeluxProductPosition position;
790 // on and inside range limits
791 assertTrue(new VeluxProductPosition(VeluxProductPosition.VPP_VELUX_MIN).isValid());
792 assertTrue(new VeluxProductPosition(VeluxProductPosition.VPP_VELUX_MAX).isValid());
793 assertTrue(new VeluxProductPosition(VeluxProductPosition.VPP_VELUX_MAX - 1).isValid());
794 assertTrue(new VeluxProductPosition(VeluxProductPosition.VPP_VELUX_MIN + 1).isValid());
796 // outside range limits
797 assertFalse(new VeluxProductPosition(VeluxProductPosition.VPP_VELUX_MIN - 1).isValid());
798 assertFalse(new VeluxProductPosition(VeluxProductPosition.VPP_VELUX_MAX + 1).isValid());
799 assertFalse(new VeluxProductPosition(VeluxProductPosition.VPP_VELUX_IGNORE).isValid());
800 assertFalse(new VeluxProductPosition(VeluxProductPosition.VPP_VELUX_DEFAULT).isValid());
801 assertFalse(new VeluxProductPosition(VeluxProductPosition.VPP_VELUX_STOP).isValid());
802 assertFalse(new VeluxProductPosition(VeluxProductPosition.VPP_VELUX_UNKNOWN).isValid());
804 // 80% absolute position
805 position = new VeluxProductPosition(new PercentType(80));
806 assertEquals(0xA000, position.getPositionAsVeluxType());
807 assertTrue(position.isValid());
809 // 80% absolute position
810 position = new VeluxProductPosition(new PercentType(80), false);
811 assertEquals(0xA000, position.getPositionAsVeluxType());
812 assertTrue(position.isValid());
814 // 80% inverted absolute position (i.e. 20%)
815 position = new VeluxProductPosition(new PercentType(80), true);
816 assertEquals(0x2800, position.getPositionAsVeluxType());
817 assertTrue(position.isValid());
819 // 80% positive relative position
820 target = VeluxProductPosition.VPP_VELUX_RELATIVE_ORIGIN
821 + (VeluxProductPosition.VPP_VELUX_RELATIVE_RANGE * 8 / 10);
822 position = new VeluxProductPosition(new PercentType(80)).overridePositionType(PositionType.OFFSET_POSITIVE);
823 assertTrue(position.isValid());
824 assertEquals(target, position.getPositionAsVeluxType());
826 // 80% negative relative position
827 target = VeluxProductPosition.VPP_VELUX_RELATIVE_ORIGIN
828 - (VeluxProductPosition.VPP_VELUX_RELATIVE_RANGE * 8 / 10);
829 position = new VeluxProductPosition(new PercentType(80)).overridePositionType(PositionType.OFFSET_NEGATIVE);
830 assertTrue(position.isValid());
831 assertEquals(target, position.getPositionAsVeluxType());
835 * Test SCrunProductResult results
839 public void testSCrunProductResults() {
840 SCrunProductCommand bcp = new SCrunProductCommand();
842 // create a dummy product to get some functional parameters from
843 VeluxProduct product = new VeluxProduct(VeluxProductName.UNKNOWN, new ProductBridgeIndex(PRODUCT_INDEX_A), 0, 0,
844 0, null, Command.UNDEFTYPE);
845 product.setActuatorType(ACTUATOR_TYPE_SOMFY);
846 product.setVanePosition(VANE_POSITION_A);
847 final FunctionalParameters functionalParameters = product.getFunctionalParameters();
851 // test setting both main and vane position
852 ok = bcp.setNodeIdAndParameters(PRODUCT_INDEX_A, new VeluxProductPosition(MAIN_POSITION_A),
853 functionalParameters);
855 product = bcp.getProduct();
856 product.setActuatorType(ACTUATOR_TYPE_SOMFY);
857 assertEquals(MAIN_POSITION_A, product.getCurrentPosition());
858 assertEquals(VANE_POSITION_A, product.getVanePosition());
860 // test setting vane position only
861 ok = bcp.setNodeIdAndParameters(PRODUCT_INDEX_A, null, functionalParameters);
863 product = bcp.getProduct();
864 product.setActuatorType(ACTUATOR_TYPE_SOMFY);
865 assertEquals(IGNORE_POSITION, product.getCurrentPosition());
866 assertEquals(VANE_POSITION_A, product.getVanePosition());
868 // test setting main position only
869 ok = bcp.setNodeIdAndParameters(PRODUCT_INDEX_A, new VeluxProductPosition(MAIN_POSITION_A), null);
871 product = bcp.getProduct();
872 product.setActuatorType(ACTUATOR_TYPE_SOMFY);
873 assertEquals(MAIN_POSITION_A, product.getCurrentPosition());
874 assertEquals(UNKNOWN_POSITION, product.getVanePosition());
876 // test setting neither
877 ok = bcp.setNodeIdAndParameters(PRODUCT_INDEX_A, null, null);
882 * Test SCgetProductStatus exceptional error state processing.
886 public void testErrorStateMapping() {
887 // initialise the test parameters
888 final String packet = "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"
889 + " 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"
891 final Command command = VeluxKLFAPI.Command.GW_STATUS_REQUEST_NTF;
893 // initialise the BCP
894 SCgetProductStatus bcp = new SCgetProductStatus();
895 bcp.setProductId(PRODUCT_INDEX_A);
897 // set the packet response
898 bcp.setResponse(command.getShort(), toByteArray(packet), false);
901 assertTrue(bcp.isCommunicationSuccessful());
902 assertTrue(bcp.isCommunicationFinished());
904 // check the product state
905 assertEquals(ProductState.UNKNOWN.value, bcp.getProduct().getState());