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.internal.handler;
15 import org.eclipse.jdt.annotation.NonNullByDefault;
16 import org.eclipse.jdt.annotation.Nullable;
17 import org.openhab.binding.velux.internal.VeluxBindingProperties;
18 import org.openhab.binding.velux.internal.bridge.VeluxBridgeGetLimitation;
19 import org.openhab.binding.velux.internal.bridge.VeluxBridgeSetLimitation;
20 import org.openhab.binding.velux.internal.handler.utils.ThingConfiguration;
21 import org.openhab.binding.velux.internal.things.VeluxProduct;
22 import org.openhab.binding.velux.internal.things.VeluxProductPosition;
23 import org.openhab.binding.velux.internal.things.VeluxProductSerialNo;
24 import org.openhab.core.library.types.PercentType;
25 import org.openhab.core.thing.ChannelUID;
26 import org.openhab.core.types.Command;
27 import org.openhab.core.types.State;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
32 * <B>Channel-specific retrieval and modification.</B>
34 * This class implements the Channel <B>position</B> of the Thing <B>actuator</B>:
36 * <LI><I>Velux</I> <B>bridge</B> → <B>OpenHAB</B>:
38 * Information retrieval by method {@link #handleRefresh}.</LI>
41 * <LI><B>OpenHAB</B> Event Bus → <I>Velux</I> <B>bridge</B>
43 * Sending commands and value updates by method {@link #handleCommand}.</LI>
46 * @author Guenther Schreiner - Initial contribution.
49 final class ChannelActuatorLimitation extends ChannelHandlerTemplate {
50 private static final Logger LOGGER = LoggerFactory.getLogger(ChannelActuatorLimitation.class);
53 * ************************
54 * ***** Constructors *****
57 // Suppress default constructor for non-instantiability
59 private ChannelActuatorLimitation() {
60 throw new AssertionError();
64 * Communication method to retrieve information to update the channel value.
66 * @param channelUID The item passed as type {@link ChannelUID} for which a refresh is intended.
67 * @param channelId The same item passed as type {@link String} for which a refresh is intended.
68 * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides
69 * information for this channel.
70 * @return newState The value retrieved for the passed channel, or <I>null</I> in case if there is no (new) value.
72 static @Nullable State handleRefresh(ChannelUID channelUID, String channelId,
73 VeluxBridgeHandler thisBridgeHandler) {
74 LOGGER.debug("handleRefresh({},{},{}) called.", channelUID, channelId, thisBridgeHandler);
75 State newState = null;
76 do { // just for common exit
77 boolean setMinimum = channelId.length() == 0;
78 if (thisBridgeHandler.bridgeParameters.actuators.autoRefresh(thisBridgeHandler.thisBridge)) {
79 LOGGER.trace("handleRefresh(): there are some existing products.");
81 if (!ThingConfiguration.exists(thisBridgeHandler, channelUID,
82 VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER)) {
83 LOGGER.trace("handleRefresh(): aborting processing as {} is not set.",
84 VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER);
87 String actuatorSerial = (String) ThingConfiguration.getValue(thisBridgeHandler, channelUID,
88 VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER);
89 LOGGER.trace("handleRefresh(): actuatorSerial={}", actuatorSerial);
91 // Handle value inversion
92 boolean propertyInverted = false;
93 if (ThingConfiguration.exists(thisBridgeHandler, channelUID,
94 VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED)) {
95 propertyInverted = (boolean) ThingConfiguration.getValue(thisBridgeHandler, channelUID,
96 VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED);
98 boolean isInverted = propertyInverted || VeluxProductSerialNo.indicatesRevertedValues(actuatorSerial);
99 LOGGER.trace("handleRefresh(): isInverted={}.", isInverted);
100 actuatorSerial = VeluxProductSerialNo.cleaned(actuatorSerial);
102 if (!thisBridgeHandler.bridgeParameters.actuators.getChannel().existingProducts
103 .isRegistered(actuatorSerial)) {
104 LOGGER.info("handleRefresh(): cannot work on unknown actuator with serial {}.", actuatorSerial);
107 LOGGER.trace("handleRefresh(): fetching actuator for {}.", actuatorSerial);
108 VeluxProduct thisProduct = thisBridgeHandler.bridgeParameters.actuators.getChannel().existingProducts
109 .get(actuatorSerial);
110 LOGGER.trace("handleRefresh(): found actuator {}.", thisProduct);
112 VeluxBridgeGetLimitation getLimitation = new VeluxBridgeGetLimitation();
115 success = getLimitation.getMinimumLimitation(thisBridgeHandler.thisBridge,
116 thisProduct.getBridgeProductIndex().toInt());
118 success = getLimitation.getMaximumLimitation(thisBridgeHandler.thisBridge,
119 thisProduct.getBridgeProductIndex().toInt());
122 LOGGER.info("handleRefresh(): retrieval failed.");
125 VeluxProductPosition position = getLimitation.getLimitation();
126 if (position.isValid()) {
127 PercentType positionAsPercent = position.getPositionAsPercentType(isInverted);
128 LOGGER.trace("handleRefresh(): found limitation of actuator at level {}.", positionAsPercent);
129 newState = positionAsPercent;
131 LOGGER.trace("handleRefresh(): limitation level of actuator is unknown.");
134 } while (false); // common exit
135 LOGGER.trace("handleRefresh() returns {}.", newState);
140 * Communication method to update the real world according to the passed channel value (or command).
142 * @param channelUID The item passed as type {@link ChannelUID} for which to following command is addressed to.
143 * @param channelId The same item passed as type {@link String} for which a refresh is intended.
144 * @param command The command passed as type {@link Command} for the mentioned item.
145 * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides
146 * information for this channel.
147 * @return newValue ...
149 static @Nullable Command handleCommand(ChannelUID channelUID, String channelId, Command command,
150 VeluxBridgeHandler thisBridgeHandler) {
151 LOGGER.debug("handleCommand({},{},{},{}) called.", channelUID, channelId, command, thisBridgeHandler);
152 Command newValue = null;
153 do { // just for common exit
154 boolean setMinimum = channelId.length() == 0;
155 if (thisBridgeHandler.bridgeParameters.actuators.autoRefresh(thisBridgeHandler.thisBridge)) {
156 LOGGER.trace("handleCommand(): there are some existing products.");
158 if (!ThingConfiguration.exists(thisBridgeHandler, channelUID,
159 VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER)) {
160 LOGGER.trace("handleCommand(): aborting processing as {} is not set.",
161 VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER);
164 String actuatorSerial = (String) ThingConfiguration.getValue(thisBridgeHandler, channelUID,
165 VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER);
166 LOGGER.trace("handleCommand(): actuatorSerial={}", actuatorSerial);
168 // Handle value inversion
169 boolean propertyInverted = false;
170 if (ThingConfiguration.exists(thisBridgeHandler, channelUID,
171 VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED)) {
172 propertyInverted = (boolean) ThingConfiguration.getValue(thisBridgeHandler, channelUID,
173 VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED);
175 boolean isInverted = propertyInverted || VeluxProductSerialNo.indicatesRevertedValues(actuatorSerial);
176 LOGGER.trace("handleCommand(): isInverted={}.", isInverted);
177 actuatorSerial = VeluxProductSerialNo.cleaned(actuatorSerial);
179 if (!thisBridgeHandler.bridgeParameters.actuators.getChannel().existingProducts
180 .isRegistered(actuatorSerial)) {
181 LOGGER.info("handleCommand(): cannot work on unknown actuator with serial {}.", actuatorSerial);
184 LOGGER.trace("handleCommand(): fetching actuator for {}.", actuatorSerial);
185 VeluxProduct thisProduct = thisBridgeHandler.bridgeParameters.actuators.getChannel().existingProducts
186 .get(actuatorSerial);
187 LOGGER.trace("handleCommand(): found actuator {}.", thisProduct);
189 if (!(command instanceof PercentType)) {
190 LOGGER.trace("handleCommand(): aborting processing as command is not of type PercentType.");
193 LOGGER.trace("handleCommand(): found command of type PercentType.");
194 VeluxProductPosition posCommand = new VeluxProductPosition((PercentType) command, isInverted);
195 LOGGER.trace("handleCommand(): found command to set level to {}.", posCommand);
198 new VeluxBridgeSetLimitation().setMinimumLimitation(thisBridgeHandler.thisBridge,
199 thisProduct.getBridgeProductIndex().toInt(), posCommand);
201 new VeluxBridgeSetLimitation().setMaximumLimitation(thisBridgeHandler.thisBridge,
202 thisProduct.getBridgeProductIndex().toInt(), posCommand);
204 } while (false); // common exit
205 LOGGER.trace("handleCommand() returns {}.", newValue);