]> git.basschouten.com Git - openhab-addons.git/blob
18f91f918791bbbb808aa39f069e16ce6b878afb
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2022 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.velux.internal.things;
14
15 import org.eclipse.jdt.annotation.NonNullByDefault;
16 import org.slf4j.Logger;
17 import org.slf4j.LoggerFactory;
18
19 /**
20  * <B>Velux</B> product representation.
21  * <P>
22  * Combined set of information describing a single Velux product.
23  *
24  * @author Guenther Schreiner - initial contribution.
25  */
26 @NonNullByDefault
27 public class VeluxProduct {
28     private final Logger logger = LoggerFactory.getLogger(VeluxProduct.class);
29
30     // Public definition
31
32     public static final VeluxProduct UNKNOWN = new VeluxProduct();
33
34     // Type definitions
35
36     public static class ProductBridgeIndex {
37
38         // Public definition
39         public static final ProductBridgeIndex UNKNOWN = new ProductBridgeIndex(0);
40
41         // Class internal
42         private int id;
43
44         // Constructor
45         public ProductBridgeIndex(int id) {
46             this.id = id;
47         }
48
49         // Class access methods
50         public int toInt() {
51             return id;
52         }
53
54         @Override
55         public String toString() {
56             return Integer.toString(id);
57         }
58     }
59
60     // State (of movement) of an actuator
61     public static enum State {
62         NON_EXECUTING(0),
63         ERROR(1),
64         NOT_USED(2),
65         WAITING_FOR_POWER(3),
66         EXECUTING(4),
67         DONE(5),
68         MANUAL_OVERRIDE(0x80),
69         UNKNOWN(0xFF);
70
71         public final int value;
72
73         private State(int value) {
74             this.value = value;
75         }
76     }
77
78     // Class internal
79
80     private VeluxProductName name;
81     private VeluxProductType typeId;
82     private ProductBridgeIndex bridgeProductIndex;
83
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;
96
97     // Constructor
98
99     /**
100      * Constructor
101      *
102      * just for the dummy VeluxProduct.
103      */
104     public VeluxProduct() {
105         logger.trace("VeluxProduct() created.");
106         this.name = VeluxProductName.UNKNOWN;
107         this.typeId = VeluxProductType.UNDEFTYPE;
108         this.bridgeProductIndex = ProductBridgeIndex.UNKNOWN;
109     }
110
111     /**
112      * Constructor
113      *
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.
119      */
120     public VeluxProduct(VeluxProductName name, VeluxProductType typeId, ProductBridgeIndex bridgeProductIndex) {
121         logger.trace("VeluxProduct(v1,name={}) created.", name.toString());
122         this.name = name;
123         this.typeId = typeId;
124         this.bridgeProductIndex = bridgeProductIndex;
125     }
126
127     /**
128      * Constructor
129      *
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.
147      */
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());
152         this.name = name;
153         this.typeId = typeId;
154         this.bridgeProductIndex = bridgeProductIndex;
155         this.v2 = true;
156         this.order = order;
157         this.placement = placement;
158         this.velocity = velocity;
159         this.variation = variation;
160         this.powerMode = powerMode;
161         this.serialNumber = serialNumber;
162         this.state = state;
163         this.currentPosition = currentPosition;
164         this.targetPosition = target;
165         this.remainingTime = remainingTime;
166         this.timeStamp = timeStamp;
167     }
168
169     // Utility methods
170
171     @Override
172     public VeluxProduct clone() {
173         if (this.v2) {
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);
177         } else {
178             return new VeluxProduct(this.name, this.typeId, this.bridgeProductIndex);
179         }
180     }
181
182     // Class access methods
183
184     /**
185      * Returns the name of the current product (aka actuator) for convenience as type-specific class.
186      *
187      * @return nameOfThisProduct as type {@link VeluxProductName}.
188      */
189     public VeluxProductName getProductName() {
190         return this.name;
191     }
192
193     /**
194      * Returns the type of the current product (aka actuator) for convenience as type-specific class.
195      *
196      * @return typeOfThisProduct as type {@link VeluxProductType}.
197      */
198     public VeluxProductType getProductType() {
199         return this.typeId;
200     }
201
202     public ProductBridgeIndex getBridgeProductIndex() {
203         return this.bridgeProductIndex;
204     }
205
206     @Override
207     public String toString() {
208         if (this.v2) {
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);
211         } else {
212             return String.format("Product \"%s\" / %s (bridgeIndex %d)", this.name, this.typeId,
213                     this.bridgeProductIndex.toInt());
214         }
215     }
216
217     // Class helper methods
218
219     public String getProductUniqueIndex() {
220         if (!v2 || serialNumber.startsWith(VeluxProductSerialNo.UNKNOWN)) {
221             return name.toString();
222         }
223         return VeluxProductSerialNo.cleaned(serialNumber);
224     }
225
226     // Getter and Setter methods
227
228     /**
229      * @return <b>v2</b> as type boolean signals the availability of firmware version two (product) details.
230      */
231     public boolean isV2() {
232         return v2;
233     }
234
235     /**
236      * @return <b>order</b> as type int describes the user-oriented sort-order.
237      */
238     public int getOrder() {
239         return order;
240     }
241
242     /**
243      * @return <B>placement</B> as type int is used to describe a group index or house group index number.
244      */
245     public int getPlacement() {
246         return placement;
247     }
248
249     /**
250      * @return <B>velocity</B> as type int describes what velocity the node is operation with
251      */
252     public int getVelocity() {
253         return velocity;
254     }
255
256     /**
257      * @return <B>variation</B> as type int describes detail information like top hung, kip, flat roof or sky light
258      *         window.
259      */
260     public int getVariation() {
261         return variation;
262     }
263
264     /**
265      * @return <B>powerMode</B> as type int is used to show the power mode of the node (ALWAYS_ALIVE/LOW_POWER_MODE).
266      */
267     public int getPowerMode() {
268         return powerMode;
269     }
270
271     /**
272      * @return <B>serialNumber</B> as type String is the serial number of 8 bytes length of the node.
273      */
274     public String getSerialNumber() {
275         return serialNumber;
276     }
277
278     /**
279      * @return <B>state</B> as type int is used to operating state of the node.
280      */
281     public int getState() {
282         return state;
283     }
284
285     /**
286      * @param newState Update the operating state of the node.
287      * @return <B>modified</B> as type boolean to signal a real modification.
288      */
289     public boolean setState(int newState) {
290         if (this.state == newState) {
291             return false;
292         } else {
293             logger.trace("setState(name={},index={}) state {} replaced by {}.", name.toString(),
294                     bridgeProductIndex.toInt(), this.state, newState);
295             this.state = newState;
296             return true;
297         }
298     }
299
300     /**
301      * @return <B>currentPosition</B> as type int signals the current position of the node.
302      */
303     public int getCurrentPosition() {
304         return currentPosition;
305     }
306
307     /**
308      * @param newCurrentPosition Update the current position of the node.
309      * @return <B>modified</B> as boolean to signal a real modification.
310      */
311     public boolean setCurrentPosition(int newCurrentPosition) {
312         if (this.currentPosition == newCurrentPosition) {
313             return false;
314         } else {
315             logger.trace("setCurrentPosition(name={},index={}) currentPosition {} replaced by {}.", name.toString(),
316                     bridgeProductIndex.toInt(), this.currentPosition, newCurrentPosition);
317             this.currentPosition = newCurrentPosition;
318             return true;
319         }
320     }
321
322     /**
323      * @return <b>target</b> as type int shows the target position of the current operation.
324      */
325     public int getTarget() {
326         return targetPosition;
327     }
328
329     /**
330      * @param newTarget Update the target position of the current operation.
331      * @return <b>modified</b> as boolean to signal a real modification.
332      */
333     public boolean setTarget(int newTarget) {
334         if (this.targetPosition == newTarget) {
335             return false;
336         } else {
337             logger.trace("setCurrentPosition(name={},index={}) target {} replaced by {}.", name.toString(),
338                     bridgeProductIndex.toInt(), this.targetPosition, newTarget);
339             this.targetPosition = newTarget;
340             return true;
341         }
342     }
343
344     /**
345      * @return <b>remainingTime</b> as type int describes the intended remaining time of current operation.
346      */
347     public int getRemainingTime() {
348         return remainingTime;
349     }
350
351     /**
352      * @return <b>timeStamp</b> as type int describes the current time.
353      */
354     public int getTimeStamp() {
355         return timeStamp;
356     }
357
358     /**
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
364      *
365      * @return The display position of the actuator
366      */
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;
371         }
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;
379             }
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;
384             }
385         }
386         return currentPosition;
387     }
388 }