2 * Copyright (c) 2010-2021 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.internal.things;
15 import org.eclipse.jdt.annotation.NonNullByDefault;
16 import org.slf4j.Logger;
17 import org.slf4j.LoggerFactory;
20 * <B>Velux</B> product representation.
22 * Combined set of information describing a single Velux product.
24 * @author Guenther Schreiner - initial contribution.
27 public class VeluxProduct {
28 private final Logger logger = LoggerFactory.getLogger(VeluxProduct.class);
32 public static final VeluxProduct UNKNOWN = new VeluxProduct();
36 public static class ProductBridgeIndex {
39 public static final ProductBridgeIndex UNKNOWN = new ProductBridgeIndex(0);
45 public ProductBridgeIndex(int id) {
49 // Class access methods
55 public String toString() {
56 return Integer.toString(id);
60 // State (of movement) of an actuator
61 public static enum State {
68 MANUAL_OVERRIDE(0x80),
71 public final int value;
73 private State(int value) {
80 private VeluxProductName name;
81 private VeluxProductType typeId;
82 private ProductBridgeIndex bridgeProductIndex;
84 private boolean v2 = false;
85 private int order = 0;
86 private int placement = 0;
87 private int velocity = 0;
88 private int variation = 0;
89 private int powerMode = 0;
90 private String serialNumber = VeluxProductSerialNo.UNKNOWN;
91 private int state = State.UNKNOWN.value;
92 private int currentPosition = 0;
93 private int targetPosition = 0;
94 private int remainingTime = 0;
95 private int timeStamp = 0;
102 * just for the dummy VeluxProduct.
104 public VeluxProduct() {
105 logger.trace("VeluxProduct() created.");
106 this.name = VeluxProductName.UNKNOWN;
107 this.typeId = VeluxProductType.UNDEFTYPE;
108 this.bridgeProductIndex = ProductBridgeIndex.UNKNOWN;
114 * @param name This field Name holds the name of the actuator, ex. “Window 1”. This field is 64 bytes
115 * long, formatted as UTF-8 characters.
116 * @param typeId This field indicates the node type, ex. Window, Roller shutter, Light etc.
117 * @param bridgeProductIndex NodeID is an Actuator index in the system table, to get information from. It must be a
118 * value from 0 to 199.
120 public VeluxProduct(VeluxProductName name, VeluxProductType typeId, ProductBridgeIndex bridgeProductIndex) {
121 logger.trace("VeluxProduct(v1,name={}) created.", name.toString());
123 this.typeId = typeId;
124 this.bridgeProductIndex = bridgeProductIndex;
130 * @param name This field Name holds the name of the actuator, ex. “Window 1”. This field is 64 bytes
131 * long, formatted as UTF-8 characters.
132 * @param typeId This field indicates the node type, ex. Window, Roller shutter, Light etc.
133 * @param bridgeProductIndex NodeID is an Actuator index in the system table, to get information from. It must be a
134 * value from 0 to 199.
135 * @param order Order can be used to store a sort order. The sort order is used in client end, when
136 * presenting a list of nodes for the user.
137 * @param placement Placement can be used to store a room group index or house group index number.
138 * @param velocity This field indicates what velocity the node is operation with.
139 * @param variation More detail information like top hung, kip, flat roof or sky light window.
140 * @param powerMode This field indicates the power mode of the node (ALWAYS_ALIVE/LOW_POWER_MODE).
141 * @param serialNumber This field tells the serial number of the node. This field is 8 bytes.
142 * @param state This field indicates the operating state of the node.
143 * @param currentPosition This field indicates the current position of the node.
144 * @param target This field indicates the target position of the current operation.
145 * @param remainingTime This field indicates the remaining time for a node activation in seconds.
146 * @param timeStamp UTC time stamp for last known position.
148 public VeluxProduct(VeluxProductName name, VeluxProductType typeId, ProductBridgeIndex bridgeProductIndex,
149 int order, int placement, int velocity, int variation, int powerMode, String serialNumber, int state,
150 int currentPosition, int target, int remainingTime, int timeStamp) {
151 logger.trace("VeluxProduct(v2,name={}) created.", name.toString());
153 this.typeId = typeId;
154 this.bridgeProductIndex = bridgeProductIndex;
157 this.placement = placement;
158 this.velocity = velocity;
159 this.variation = variation;
160 this.powerMode = powerMode;
161 this.serialNumber = serialNumber;
163 this.currentPosition = currentPosition;
164 this.targetPosition = target;
165 this.remainingTime = remainingTime;
166 this.timeStamp = timeStamp;
172 public VeluxProduct clone() {
174 return new VeluxProduct(this.name, this.typeId, this.bridgeProductIndex, this.order, this.placement,
175 this.velocity, this.variation, this.powerMode, this.serialNumber, this.state, this.currentPosition,
176 this.targetPosition, this.remainingTime, this.timeStamp);
178 return new VeluxProduct(this.name, this.typeId, this.bridgeProductIndex);
182 // Class access methods
185 * Returns the name of the current product (aka actuator) for convenience as type-specific class.
187 * @return nameOfThisProduct as type {@link VeluxProductName}.
189 public VeluxProductName getProductName() {
194 * Returns the type of the current product (aka actuator) for convenience as type-specific class.
196 * @return typeOfThisProduct as type {@link VeluxProductType}.
198 public VeluxProductType getProductType() {
202 public ProductBridgeIndex getBridgeProductIndex() {
203 return this.bridgeProductIndex;
207 public String toString() {
209 return String.format("Product \"%s\" / %s (bridgeIndex=%d,serial=%s,position=%04X)", this.name, this.typeId,
210 this.bridgeProductIndex.toInt(), this.serialNumber, this.currentPosition);
212 return String.format("Product \"%s\" / %s (bridgeIndex %d)", this.name, this.typeId,
213 this.bridgeProductIndex.toInt());
217 // Class helper methods
219 public String getProductUniqueIndex() {
220 if (!v2 || serialNumber.startsWith(VeluxProductSerialNo.UNKNOWN)) {
221 return name.toString();
223 return VeluxProductSerialNo.cleaned(serialNumber);
226 // Getter and Setter methods
229 * @return <b>v2</b> as type boolean signals the availability of firmware version two (product) details.
231 public boolean isV2() {
236 * @return <b>order</b> as type int describes the user-oriented sort-order.
238 public int getOrder() {
243 * @return <B>placement</B> as type int is used to describe a group index or house group index number.
245 public int getPlacement() {
250 * @return <B>velocity</B> as type int describes what velocity the node is operation with
252 public int getVelocity() {
257 * @return <B>variation</B> as type int describes detail information like top hung, kip, flat roof or sky light
260 public int getVariation() {
265 * @return <B>powerMode</B> as type int is used to show the power mode of the node (ALWAYS_ALIVE/LOW_POWER_MODE).
267 public int getPowerMode() {
272 * @return <B>serialNumber</B> as type String is the serial number of 8 bytes length of the node.
274 public String getSerialNumber() {
279 * @return <B>state</B> as type int is used to operating state of the node.
281 public int getState() {
286 * @param newState Update the operating state of the node.
287 * @return <B>modified</B> as type boolean to signal a real modification.
289 public boolean setState(int newState) {
290 if (this.state == newState) {
293 logger.trace("setState(name={},index={}) state {} replaced by {}.", name.toString(),
294 bridgeProductIndex.toInt(), this.state, newState);
295 this.state = newState;
301 * @return <B>currentPosition</B> as type int signals the current position of the node.
303 public int getCurrentPosition() {
304 return currentPosition;
308 * @param newCurrentPosition Update the current position of the node.
309 * @return <B>modified</B> as boolean to signal a real modification.
311 public boolean setCurrentPosition(int newCurrentPosition) {
312 if (this.currentPosition == newCurrentPosition) {
315 logger.trace("setCurrentPosition(name={},index={}) currentPosition {} replaced by {}.", name.toString(),
316 bridgeProductIndex.toInt(), this.currentPosition, newCurrentPosition);
317 this.currentPosition = newCurrentPosition;
323 * @return <b>target</b> as type int shows the target position of the current operation.
325 public int getTarget() {
326 return targetPosition;
330 * @param newTarget Update the target position of the current operation.
331 * @return <b>modified</b> as boolean to signal a real modification.
333 public boolean setTarget(int newTarget) {
334 if (this.targetPosition == newTarget) {
337 logger.trace("setCurrentPosition(name={},index={}) target {} replaced by {}.", name.toString(),
338 bridgeProductIndex.toInt(), this.targetPosition, newTarget);
339 this.targetPosition = newTarget;
345 * @return <b>remainingTime</b> as type int describes the intended remaining time of current operation.
347 public int getRemainingTime() {
348 return remainingTime;
352 * @return <b>timeStamp</b> as type int describes the current time.
354 public int getTimeStamp() {
359 * Returns the display position of the actuator.
360 * <li>As a general rule it returns <b>currentPosition</b>, except as follows..
361 * <li>If the actuator is in a motion state it returns <b>targetPosition</b>
362 * <li>If the motion state is 'done' but the currentPosition is invalid it returns <b>targetPosition</b>
363 * <li>If the manual override flag is set it returns the <b>unknown</b> position value
365 * @return The display position of the actuator
367 public int getDisplayPosition() {
368 // manual override flag set: position is 'unknown'
369 if ((state & State.MANUAL_OVERRIDE.value) != 0) {
370 return VeluxProductPosition.VPP_VELUX_UNKNOWN;
372 // only check other conditions if targetPosition is valid and differs from currentPosition
373 if ((targetPosition != currentPosition) && (targetPosition <= VeluxProductPosition.VPP_VELUX_MAX)
374 && (targetPosition >= VeluxProductPosition.VPP_VELUX_MIN)) {
375 int state = this.state & 0xf;
376 // actuator is in motion: for quicker UI update, return targetPosition
377 if ((state > State.ERROR.value) && (state < State.DONE.value)) {
378 return targetPosition;
380 // motion complete but currentPosition is not valid: return targetPosition
381 if ((state == State.DONE.value) && ((currentPosition > VeluxProductPosition.VPP_VELUX_MAX)
382 || (currentPosition < VeluxProductPosition.VPP_VELUX_MIN))) {
383 return targetPosition;
386 return currentPosition;