]> git.basschouten.com Git - openhab-addons.git/blob
d5cba669abb6562c408db69e0d3c1c8d69e0ec7e
[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.handler;
14
15 import static org.openhab.binding.velux.internal.VeluxBindingConstants.*;
16
17 import java.util.Arrays;
18 import java.util.List;
19
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.eclipse.jdt.annotation.Nullable;
22 import org.openhab.binding.velux.internal.bridge.common.GetProduct;
23 import org.openhab.binding.velux.internal.bridge.common.RunProductCommand;
24 import org.openhab.binding.velux.internal.bridge.slip.FunctionalParameters;
25 import org.openhab.binding.velux.internal.bridge.slip.SCrunProductCommand;
26 import org.openhab.binding.velux.internal.handler.utils.Thing2VeluxActuator;
27 import org.openhab.binding.velux.internal.things.VeluxExistingProducts;
28 import org.openhab.binding.velux.internal.things.VeluxProduct;
29 import org.openhab.binding.velux.internal.things.VeluxProduct.ProductBridgeIndex;
30 import org.openhab.binding.velux.internal.things.VeluxProduct.ProductState;
31 import org.openhab.binding.velux.internal.things.VeluxProductPosition;
32 import org.openhab.core.library.types.OnOffType;
33 import org.openhab.core.library.types.PercentType;
34 import org.openhab.core.library.types.StopMoveType;
35 import org.openhab.core.library.types.UpDownType;
36 import org.openhab.core.thing.ChannelUID;
37 import org.openhab.core.types.Command;
38 import org.openhab.core.types.State;
39 import org.openhab.core.types.UnDefType;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42
43 /**
44  * <B>Channel-specific retrieval and modification.</B>
45  * <P>
46  * This class implements the Channel <B>position</B> of the Thing <B>actuator</B>:
47  * <UL>
48  * <LI><I>Velux</I> <B>bridge</B> &rarr; <B>OpenHAB</B>:
49  * <P>
50  * Information retrieval by method {@link #handleRefresh}.</LI>
51  * </UL>
52  * <UL>
53  * <LI><B>OpenHAB</B> Event Bus &rarr; <I>Velux</I> <B>bridge</B>
54  * <P>
55  * Sending commands and value updates by method {@link #handleCommand}.</LI>
56  * </UL>
57  *
58  * @author Guenther Schreiner - Initial contribution.
59  * @author Andrew Fiddian-Green - Refactoring and use alternate API set for Vane Position.
60  */
61 @NonNullByDefault
62 final class ChannelActuatorPosition extends ChannelHandlerTemplate {
63     private static final Logger LOGGER = LoggerFactory.getLogger(ChannelActuatorPosition.class);
64
65     // Constructors
66
67     /**
68      * Suppress default constructor for non-instantiability.
69      */
70     private ChannelActuatorPosition() {
71         throw new AssertionError();
72     }
73
74     /*
75      * List of product states that shall be processed
76      */
77     private static final List<ProductState> STATES_TO_PROCESS = Arrays.asList(ProductState.DONE, ProductState.EXECUTING,
78             ProductState.MANUAL, ProductState.UNKNOWN);
79
80     // Public methods
81
82     /**
83      * Communication method to retrieve information to update the channel value.
84      *
85      * @param channelUID The item passed as type {@link ChannelUID} for which a refresh is intended.
86      * @param channelId The same item passed as type {@link String} for which a refresh is intended.
87      * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides
88      *            information for this channel.
89      * @return newState The value retrieved for the passed channel, or <I>null</I> in case if there is no (new) value.
90      */
91     static @Nullable State handleRefresh(ChannelUID channelUID, String channelId,
92             VeluxBridgeHandler thisBridgeHandler) {
93         LOGGER.debug("handleRefresh({},{},{}) called.", channelUID, channelId, thisBridgeHandler);
94         State newState = null;
95         do { // just for common exit
96             if (thisBridgeHandler.bridgeParameters.actuators.autoRefresh(thisBridgeHandler.thisBridge)) {
97                 LOGGER.trace("handleRefresh(): there are some existing products.");
98             }
99
100             Thing2VeluxActuator veluxActuator = thisBridgeHandler.channel2VeluxActuator.get(channelUID);
101             if (veluxActuator == null || !veluxActuator.isKnown()) {
102                 LOGGER.warn("handleRefresh(): unknown actuator.");
103                 break;
104             }
105
106             GetProduct bcp = null;
107             switch (channelId) {
108                 case CHANNEL_VANE_POSITION:
109                     bcp = thisBridgeHandler.thisBridge.bridgeAPI().getProductStatus();
110                     break;
111                 case CHANNEL_ACTUATOR_POSITION:
112                 case CHANNEL_ACTUATOR_STATE:
113                     bcp = thisBridgeHandler.thisBridge.bridgeAPI().getProduct();
114                 default:
115                     // unknown channel, will exit
116             }
117
118             if (bcp == null) {
119                 LOGGER.trace("handleRefresh(): aborting processing as handler is null.");
120                 break;
121             }
122
123             bcp.setProductId(veluxActuator.getProductBridgeIndex().toInt());
124             if ((!thisBridgeHandler.thisBridge.bridgeCommunicate(bcp)) || (!bcp.isCommunicationSuccessful())) {
125                 LOGGER.trace("handleRefresh(): bridge communication request failed.");
126                 break;
127             }
128
129             VeluxProduct newProduct = bcp.getProduct();
130             if (STATES_TO_PROCESS.contains(newProduct.getProductState())) {
131                 ProductBridgeIndex productBridgeIndex = newProduct.getBridgeProductIndex();
132                 VeluxExistingProducts existingProducts = thisBridgeHandler.existingProducts();
133                 VeluxProduct existingProduct = existingProducts.get(productBridgeIndex);
134                 if (!VeluxProduct.UNKNOWN.equals(existingProduct)) {
135                     switch (channelId) {
136                         case CHANNEL_VANE_POSITION:
137                         case CHANNEL_ACTUATOR_POSITION:
138                         case CHANNEL_ACTUATOR_STATE: {
139                             if (existingProducts.update(newProduct)) {
140                                 existingProduct = existingProducts.get(productBridgeIndex);
141                                 int posValue = VeluxProductPosition.VPP_VELUX_UNKNOWN;
142                                 switch (channelId) {
143                                     case CHANNEL_VANE_POSITION:
144                                         posValue = existingProduct.getVaneDisplayPosition();
145                                         break;
146                                     case CHANNEL_ACTUATOR_POSITION:
147                                     case CHANNEL_ACTUATOR_STATE:
148                                         posValue = existingProduct.getDisplayPosition();
149                                 }
150                                 VeluxProductPosition position = new VeluxProductPosition(posValue);
151                                 if (position.isValid()) {
152                                     switch (channelId) {
153                                         case CHANNEL_VANE_POSITION:
154                                             newState = position.getPositionAsPercentType(false);
155                                             break;
156                                         case CHANNEL_ACTUATOR_POSITION:
157                                             newState = position.getPositionAsPercentType(veluxActuator.isInverted());
158                                             break;
159                                         case CHANNEL_ACTUATOR_STATE:
160                                             newState = OnOffType
161                                                     .from(position.getPositionAsPercentType(veluxActuator.isInverted())
162                                                             .intValue() > 50);
163                                     }
164                                 }
165                             }
166                         }
167                     }
168                 }
169             }
170
171             if (newState == null) {
172                 newState = UnDefType.UNDEF;
173             }
174         } while (false); // common exit
175         LOGGER.trace("handleRefresh(): new state for channel id '{}' is '{}'.", channelId, newState);
176         return newState;
177     }
178
179     /**
180      * Communication method to update the real world according to the passed channel value (or command).
181      *
182      * @param channelUID The item passed as type {@link ChannelUID} for which to following command is addressed to.
183      * @param channelId The same item passed as type {@link String} for which a refresh is intended.
184      * @param command The command passed as type {@link Command} for the mentioned item.
185      * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides
186      *            information for this channel.
187      * @return newValue ...
188      */
189     static @Nullable Command handleCommand(ChannelUID channelUID, String channelId, Command command,
190             VeluxBridgeHandler thisBridgeHandler) {
191         LOGGER.debug("handleCommand({},{},{},{}) called.", channelUID, channelId, command, thisBridgeHandler);
192         Command newValue = null;
193         do { // just for common exit
194             if (thisBridgeHandler.bridgeParameters.actuators.autoRefresh(thisBridgeHandler.thisBridge)) {
195                 LOGGER.trace("handleCommand(): there are some existing products.");
196             }
197
198             Thing2VeluxActuator veluxActuator = thisBridgeHandler.channel2VeluxActuator.get(channelUID);
199             if (veluxActuator == null || !veluxActuator.isKnown()) {
200                 LOGGER.warn("handleRefresh(): unknown actuator.");
201                 break;
202             }
203
204             VeluxProductPosition mainParameter = null;
205             FunctionalParameters functionalParameters = null;
206             VeluxExistingProducts existingProducts = thisBridgeHandler.existingProducts();
207             ProductBridgeIndex productBridgeIndex = veluxActuator.getProductBridgeIndex();
208
209             switch (channelId) {
210                 case CHANNEL_VANE_POSITION:
211                     if (command instanceof PercentType) {
212                         VeluxProduct existingProductClone = existingProducts.get(productBridgeIndex).clone();
213                         existingProductClone.setVanePosition(
214                                 new VeluxProductPosition((PercentType) command).getPositionAsVeluxType());
215                         functionalParameters = existingProductClone.getFunctionalParameters();
216                     }
217                     break;
218
219                 case CHANNEL_ACTUATOR_POSITION:
220                     if (command instanceof UpDownType) {
221                         mainParameter = UpDownType.UP.equals(command) ^ veluxActuator.isInverted()
222                                 ? new VeluxProductPosition(PercentType.ZERO)
223                                 : new VeluxProductPosition(PercentType.HUNDRED);
224                     } else if (command instanceof StopMoveType) {
225                         mainParameter = StopMoveType.STOP.equals(command) ? new VeluxProductPosition() : mainParameter;
226                     } else if (command instanceof PercentType) {
227                         PercentType ptCommand = (PercentType) command;
228                         if (veluxActuator.isInverted()) {
229                             ptCommand = new PercentType(PercentType.HUNDRED.intValue() - ptCommand.intValue());
230                         }
231                         mainParameter = new VeluxProductPosition(ptCommand);
232                     }
233                     break;
234
235                 case CHANNEL_ACTUATOR_STATE:
236                     if (command instanceof OnOffType) {
237                         mainParameter = OnOffType.OFF.equals(command) ^ veluxActuator.isInverted()
238                                 ? new VeluxProductPosition(PercentType.ZERO)
239                                 : new VeluxProductPosition(PercentType.HUNDRED);
240                     }
241                     break;
242
243                 default:
244                     // unknown channel => do nothing..
245             }
246
247             if ((mainParameter != null) || (functionalParameters != null)) {
248                 LOGGER.debug("handleCommand(): sending command '{}' for channel id '{}'.", command, channelId);
249                 RunProductCommand bcp = thisBridgeHandler.thisBridge.bridgeAPI().runProductCommand();
250                 boolean success = false;
251                 if (bcp instanceof SCrunProductCommand) {
252                     synchronized (bcp) {
253                         if (bcp.setNodeIdAndParameters(productBridgeIndex.toInt(), mainParameter, functionalParameters)
254                                 && thisBridgeHandler.thisBridge.bridgeCommunicate(bcp)
255                                 && bcp.isCommunicationSuccessful()) {
256                             success = true;
257                             if (thisBridgeHandler.bridgeParameters.actuators
258                                     .autoRefresh(thisBridgeHandler.thisBridge)) {
259                                 LOGGER.trace("handleCommand(): actuator position will be updated via polling.");
260                             }
261                             if (existingProducts.update(((SCrunProductCommand) bcp).getProduct())) {
262                                 LOGGER.trace("handleCommand(): actuator position immediate update requested.");
263                             }
264                         }
265                     }
266                 }
267                 LOGGER.debug("handleCommand(): sendCommand() finished {}.",
268                         (success ? "successfully" : "with failure"));
269             } else {
270                 LOGGER.info("handleCommand(): ignoring command '{}' for channel id '{}'.", command, channelId);
271             }
272         } while (false); // common exit
273         return newValue;
274     }
275 }