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.hdpowerview.internal.api;
15 import static org.openhab.binding.hdpowerview.internal.api.CoordinateSystem.*;
17 import org.eclipse.jdt.annotation.NonNullByDefault;
18 import org.eclipse.jdt.annotation.Nullable;
19 import org.openhab.core.library.types.PercentType;
20 import org.openhab.core.types.*;
23 * The position of a single shade, as returned by the HD PowerView hub
25 * @author Andy Lintner - Initial contribution
26 * @author Andrew Fiddian-Green - Added support for secondary rail positions
29 public class ShadePosition {
31 * Primary actuator position
34 private int position1;
37 * Secondary actuator position
39 * here we have to use Integer objects rather than just int primitives because
40 * these are secondary optional position elements in the JSON payload, so the
41 * GSON de-serializer might leave them as null
43 private @Nullable Integer posKind2 = null;
44 private @Nullable Integer position2 = null;
47 * Create a ShadePosition position instance with just a primary actuator
50 * @param coordSys the Coordinate System to be used
51 * @param percent the percentage position within that Coordinate System
52 * @return the ShadePosition instance
54 public static ShadePosition create(CoordinateSystem coordSys, int percent) {
55 return new ShadePosition(coordSys, percent, null, null);
59 * Create a ShadePosition position instance with both a primary and a secondary
62 * @param primaryCoordSys the Coordinate System to be used for the primary
64 * @param primaryPercent the percentage position for primary position
65 * @param secondaryCoordSys the Coordinate System to be used for the secondary
67 * @param secondaryPercent the percentage position for secondary position
68 * @return the ShadePosition instance
70 public static ShadePosition create(CoordinateSystem primaryCoordSys, int primaryPercent,
71 @Nullable CoordinateSystem secondaryCoordSys, @Nullable Integer secondaryPercent) {
72 return new ShadePosition(primaryCoordSys, primaryPercent, secondaryCoordSys, secondaryPercent);
76 * Constructor for ShadePosition position with both a primary and a secondary
79 * @param primaryCoordSys the Coordinate System to be used for the primary
81 * @param primaryPercent the percentage position for primary position
82 * @param secondaryCoordSys the Coordinate System to be used for the secondary
84 * @param secondaryPercent the percentage position for secondary position
86 ShadePosition(CoordinateSystem primaryCoordSys, int primaryPercent, @Nullable CoordinateSystem secondaryCoordSys,
87 @Nullable Integer secondaryPercent) {
88 setPosition1(primaryCoordSys, primaryPercent);
89 setPosition2(secondaryCoordSys, secondaryPercent);
93 * For a given Actuator Class and Coordinate System, map the ShadePosition's
94 * state to an OpenHAB State
96 * @param actuatorClass the requested Actuator Class
97 * @param coordSys the requested Coordinate System
98 * @return the corresponding OpenHAB State
100 public State getState(ActuatorClass actuatorClass, CoordinateSystem coordSys) {
101 switch (actuatorClass) {
102 case PRIMARY_ACTUATOR:
103 return getPosition1(coordSys);
104 case SECONDARY_ACTUATOR:
105 return getPosition2(coordSys);
107 return UnDefType.UNDEF;
112 * Determine the Coordinate System used for the given Actuator Class (if any)
114 * @param actuatorClass the requested Actuator Class
115 * @return the Coordinate System used for that Actuator Class, or ERROR_UNKNOWN
116 * if the Actuator Class is not implemented
118 public CoordinateSystem getCoordinateSystem(ActuatorClass actuatorClass) {
119 switch (actuatorClass) {
120 case PRIMARY_ACTUATOR:
121 return fromPosKind(posKind1);
122 case SECONDARY_ACTUATOR:
123 Integer posKind2 = this.posKind2;
124 if (posKind2 != null) {
125 return fromPosKind(posKind2.intValue());
128 return ERROR_UNKNOWN;
132 private void setPosition1(CoordinateSystem coordSys, int percent) {
133 posKind1 = coordSys.toPosKind();
137 * Primary rail of a single action bottom-up shade, or
138 * Primary, lower, bottom-up, rail of a dual action shade
142 * Primary rail of a single action top-down shade
144 * All these types use the same coordinate system; which is inverted in relation
147 position1 = MAX_SHADE - (int) Math.round(percent / 100d * MAX_SHADE);
151 * Vane angle of the primary rail of a bottom-up single action shade
153 position1 = (int) Math.round(percent / 100d * MAX_VANE);
160 private State getPosition1(CoordinateSystem coordSys) {
164 * Primary rail of a single action bottom-up shade, or
165 * Primary, lower, bottom-up, rail of a dual action shade
169 * Primary rail of a single action top-down shade
171 * All these types use the same coordinate system; which is inverted in relation
174 * If the slats have a defined position then the shade position must by
177 return posKind1 == 3 ? PercentType.HUNDRED
178 : new PercentType(100 - (int) Math.round((double) position1 / MAX_SHADE * 100));
182 * Vane angle of the primary rail of a bottom-up single action shade
184 * If the shades are not open, the vane position is undefined; if the the shades
185 * are exactly open then the vanes are at zero; otherwise return the actual vane
188 * note: sometimes the hub may return a value of position1 > MAX_VANE (seems to
189 * be a bug in the hub) so we avoid an out of range exception via the Math.min()
192 return posKind1 != 3 ? (position1 != 0 ? UnDefType.UNDEF : PercentType.ZERO)
193 : new PercentType((int) Math.round((double) Math.min(position1, MAX_VANE) / MAX_VANE * 100));
196 return UnDefType.UNDEF;
200 private void setPosition2(@Nullable CoordinateSystem coordSys, @Nullable Integer percent) {
201 if (coordSys == null || percent == null) {
204 posKind2 = Integer.valueOf(coordSys.toPosKind());
209 * Secondary, upper, top-down rail of a dual action shade
211 * Uses a coordinate system that is NOT inverted in relation to OpenHAB
213 position2 = Integer.valueOf((int) Math.round(percent.doubleValue() / 100 * MAX_SHADE));
216 position2 = Integer.valueOf(0);
220 private State getPosition2(CoordinateSystem coordSys) {
221 Integer posKind2 = this.posKind2;
222 Integer position2 = this.position2;
223 if (position2 == null || posKind2 == null) {
224 return UnDefType.UNDEF;
229 * This case should never occur; but return a value anyway just in case
233 * Secondary, upper, top-down rail of a dual action shade
235 * Uses a coordinate system that is NOT inverted in relation to OpenHAB
237 if (posKind2.intValue() != 3) {
238 return new PercentType(100 - (int) Math.round(position2.doubleValue() / MAX_SHADE * 100));
241 return UnDefType.UNDEF;