2 * Copyright (c) 2010-2022 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.openhab.binding.velux.internal.VeluxBindingConstants;
17 import org.openhab.core.library.types.PercentType;
18 import org.slf4j.Logger;
19 import org.slf4j.LoggerFactory;
22 * <B>Velux</B> product characteristics: Product Position.
25 * "https://velcdn.azureedge.net/~/media/com/api/klf200/technical%20specification%20for%20klf%20200%20api.pdf#page=110">KLF200
26 * Standard Parameter definition</a>
28 * Methods in handle this type of information:
30 * <LI>{@link #VeluxProductPosition(int)} to convert a Velux value into the characteristic.</LI>
31 * <LI>{@link #VeluxProductPosition(PercentType)} to convert an openHAB value into the characteristic.</LI>
32 * <LI>{@link #VeluxProductPosition()} to convert an openHAB STOP value into the characteristic.</LI>
33 * <LI>{@link #isValid} to determine whether the characteristic has got a valid value.</LI>
34 * <LI>{@link #getPositionAsPercentType()} to convert the characteristic into an openHAB value.</LI>
35 * <LI>{@link #getPositionAsVeluxType()} to convert the characteristic into a Velux value.</LI>
36 * <LI>{@link #toString} to retrieve a human-readable description of this characteristic.</LI>
41 * @author Guenther Schreiner - initial contribution.
44 public class VeluxProductPosition {
45 private final Logger logger = LoggerFactory.getLogger(getClass());
49 public static final VeluxProductPosition UNKNOWN = new VeluxProductPosition();
50 public static final int VPP_VELUX_STOP = 0xD200;
51 public static final int VPP_VELUX_DEFAULT = 0xD300;
52 public static final int VPP_VELUX_IGNORE = 0xD400;
54 // Make sure that the calculation are done as non-integer
55 private static final float ONE = 1;
57 private static final int VPP_UNKNOWN = 0;
59 private static final int VPP_OPENHAB_MIN = 0;
60 private static final int VPP_OPENHAB_MAX = 100;
62 public static final int VPP_VELUX_MIN = 0x0000;
63 public static final int VPP_VELUX_MAX = 0xc800;
64 public static final int VPP_VELUX_UNKNOWN = 0xF7FF;
66 // relative mode commands
67 public static final int VPP_VELUX_RELATIVE_ORIGIN = 0xCCE8;
68 public static final int VPP_VELUX_RELATIVE_RANGE = 1000; // same for positive and negative offsets
71 * Enum that determines whether the position is an absolute value, or a positive / negative offset relative to the
74 * @author AndrewFG - Initial contribution.
76 public static enum PositionType {
83 private PositionType(float i) {
90 private PercentType position;
91 private boolean isValid = false;
92 private PositionType positionType = PositionType.ABSOLUTE_VALUE;
97 * Creation of a Position object to specify a distinct actuator setting.
99 * @param position A position as type {@link PercentType} (between 0 and 100).
101 public VeluxProductPosition(PercentType position) {
102 logger.trace("VeluxProductPosition({} as PercentType) created.", position.intValue());
103 this.position = position;
108 * Creation of a Position object to specify a distinct actuator setting.
110 * @param position A position as type {@link PercentType} (between 0 and 100).
111 * @param toBeInverted Flag whether the value should be handled as inverted.
113 public VeluxProductPosition(PercentType position, boolean toBeInverted) {
114 this(toBeInverted ? new PercentType(PercentType.HUNDRED.intValue() - position.intValue()) : position);
118 * Creation of a Position object to specify a distinct actuator setting.
120 * @param veluxPosition A position as type {@link int} based on the Velux-specific value ranges (between 0x0000 and
121 * 0xc800, or 0xD200 for stop).
123 public VeluxProductPosition(int veluxPosition) {
124 logger.trace("VeluxProductPosition(constructor with {} as veluxPosition) called.", veluxPosition);
125 if (isValid(veluxPosition)) {
126 float result = (ONE * veluxPosition - VPP_VELUX_MIN) / (VPP_VELUX_MAX - VPP_VELUX_MIN);
127 result = Math.round(VPP_OPENHAB_MIN + result * (VPP_OPENHAB_MAX - VPP_OPENHAB_MIN));
128 this.position = new PercentType((int) result);
130 logger.trace("VeluxProductPosition() created with percent-type {}.", (int) result);
132 this.position = new PercentType(VPP_UNKNOWN);
133 this.isValid = false;
134 logger.trace("VeluxProductPosition() gives up.");
138 public static boolean isValid(int position) {
139 return (position <= VeluxProductPosition.VPP_VELUX_MAX) && (position >= VeluxProductPosition.VPP_VELUX_MIN);
142 public static boolean isUnknownOrValid(int position) {
143 return (position == VeluxProductPosition.VPP_UNKNOWN) || isValid(position);
147 * Creation of a Position object to specify a STOP.
149 public VeluxProductPosition() {
150 logger.trace("VeluxProductPosition() as STOP position created.");
151 this.position = new PercentType(VPP_UNKNOWN);
152 this.isValid = false;
155 // Class access methods
157 public boolean isValid() {
161 public PercentType getPositionAsPercentType() {
165 public PercentType getPositionAsPercentType(boolean toBeInverted) {
166 return toBeInverted ? new PercentType(PercentType.HUNDRED.intValue() - position.intValue()) : position;
169 public int getPositionAsVeluxType() {
172 if (positionType == PositionType.ABSOLUTE_VALUE) {
173 result = (ONE * position.intValue() - VPP_OPENHAB_MIN) / (VPP_OPENHAB_MAX - VPP_OPENHAB_MIN);
174 result = VPP_VELUX_MIN + result * (VPP_VELUX_MAX - VPP_VELUX_MIN);
176 result = VPP_VELUX_RELATIVE_ORIGIN
177 + ((positionType.value * position.intValue() * VPP_VELUX_RELATIVE_RANGE)
178 / (VPP_OPENHAB_MAX - VPP_OPENHAB_MIN));
182 return VPP_VELUX_STOP;
187 public String toString() {
189 return String.format("%d", position.intValue());
191 return new String(VeluxBindingConstants.UNKNOWN);
197 public VeluxProductPosition overridePositionType(PositionType positionType) {
198 this.positionType = positionType;