]> git.basschouten.com Git - openhab-addons.git/blob
153e7afd2d0dd509185fd36d8f34d9898ff2c371
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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.loxone.internal.controls;
14
15 import static org.openhab.binding.loxone.internal.LxBindingConstants.*;
16
17 import java.io.IOException;
18 import java.math.BigDecimal;
19
20 import org.openhab.binding.loxone.internal.types.LxState;
21 import org.openhab.binding.loxone.internal.types.LxUuid;
22 import org.openhab.core.library.types.DecimalType;
23 import org.openhab.core.library.types.IncreaseDecreaseType;
24 import org.openhab.core.library.types.OnOffType;
25 import org.openhab.core.library.types.PercentType;
26 import org.openhab.core.thing.ChannelUID;
27 import org.openhab.core.thing.type.ChannelTypeUID;
28 import org.openhab.core.types.Command;
29 import org.openhab.core.types.StateDescriptionFragment;
30 import org.openhab.core.types.StateDescriptionFragmentBuilder;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33
34 /**
35  * A ValueSelector type of control on Loxone Miniserver.
36  * <p>
37  * According to Loxone API documentation, this control covers: Push-button +/- and Push-button + functional blocks.
38  *
39  * @author Pawel Pieczul - initial contribution
40  *
41  */
42 class LxControlValueSelector extends LxControl {
43
44     static class Factory extends LxControlInstance {
45         @Override
46         LxControl create(LxUuid uuid) {
47             return new LxControlValueSelector(uuid);
48         }
49
50         @Override
51         String getType() {
52             return "valueselector";
53         }
54     }
55
56     private static final String STATE_VALUE = "value";
57     private static final String STATE_MIN = "min";
58     private static final String STATE_MAX = "max";
59     private static final String STATE_STEP = "step";
60
61     private final Logger logger = LoggerFactory.getLogger(LxControlValueSelector.class);
62
63     private Boolean increaseOnly = false;
64     private String format = "%.1f";
65     private Double minValue;
66     private Double maxValue;
67     private Double stepValue;
68     private ChannelUID channelId;
69     private ChannelUID numberChannelId;
70
71     private LxControlValueSelector(LxUuid uuid) {
72         super(uuid);
73     }
74
75     @Override
76     public void initialize(LxControlConfig config) {
77         super.initialize(config);
78         channelId = addChannel("Dimmer", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_DIMMER),
79                 defaultChannelLabel, "Value Selector", tags, this::handleCommand, this::getChannelState);
80         numberChannelId = addChannel("Number", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_NUMBER),
81                 defaultChannelLabel + " / Number", "Value Selector by number", tags, this::handleNumberCommand,
82                 this::getChannelNumberState);
83
84         if (details != null) {
85             if (details.format != null) {
86                 this.format = details.format;
87             }
88             if (details.increaseOnly != null) {
89                 this.increaseOnly = details.increaseOnly;
90             }
91         }
92     }
93
94     private void handleCommand(Command command) throws IOException {
95         if (minValue == null || maxValue == null || minValue >= maxValue) {
96             logger.debug("Value selector min or max value missing or min>max.");
97             return;
98         }
99         if (command instanceof OnOffType) {
100             if ((OnOffType) command == OnOffType.ON) {
101                 sendAction(maxValue.toString());
102             } else {
103                 sendAction(minValue.toString());
104             }
105         } else if (command instanceof IncreaseDecreaseType) {
106             if (stepValue == null) {
107                 logger.debug("Value selector step value missing.");
108                 return;
109             }
110             IncreaseDecreaseType type = (IncreaseDecreaseType) command;
111             if (increaseOnly != null && type == IncreaseDecreaseType.DECREASE && increaseOnly) {
112                 logger.debug("Value selector configured to allow increase only.");
113                 return;
114             }
115             Double currentValue = getStateDoubleValue(STATE_VALUE);
116             if (currentValue != null) {
117                 Double nextValue = currentValue + (type == IncreaseDecreaseType.INCREASE ? stepValue : -stepValue);
118                 if (nextValue > maxValue) {
119                     nextValue = maxValue;
120                 }
121                 if (nextValue < minValue) {
122                     nextValue = minValue;
123                 }
124                 sendAction(nextValue.toString());
125             }
126         } else if (command instanceof PercentType) {
127             Double value = ((PercentType) command).doubleValue() * (maxValue - minValue) / 100.0 + minValue;
128             sendAction(value.toString());
129         }
130     }
131
132     private void handleNumberCommand(Command command) throws IOException {
133         if (minValue == null || maxValue == null || minValue >= maxValue) {
134             logger.debug("Value selector min or max value missing or min>max.");
135             return;
136         }
137         if (command instanceof DecimalType) {
138             Double value = ((DecimalType) command).doubleValue();
139             if (value < minValue || value > maxValue) {
140                 logger.debug("Value {} out of {}-{} range", value, minValue, maxValue);
141                 return;
142             }
143             sendAction(value.toString());
144         }
145     }
146
147     private PercentType getChannelState() {
148         Double value = getStateDoubleValue(STATE_VALUE);
149         if (value != null && minValue != null && maxValue != null && minValue <= value && value <= maxValue) {
150             value = ((value - minValue) * 100.0) / (maxValue - minValue);
151             return new PercentType(value.intValue());
152         }
153         return null;
154     }
155
156     private DecimalType getChannelNumberState() {
157         Double value = getStateDoubleValue(STATE_VALUE);
158         if (value != null && minValue != null && maxValue != null && minValue <= value && value <= maxValue) {
159             return new DecimalType(value);
160         }
161         return null;
162     }
163
164     /**
165      * Get dynamic updates to the minimum, maximum and step values. Sets a new state description if these values are
166      * available. If no updates to the min/max/step, calls parent class method to update selector value in the
167      * framework.
168      *
169      * @param state state update from the Miniserver
170      */
171     @Override
172     public void onStateChange(LxState state) {
173         String stateName = state.getName();
174         Object value = state.getStateValue();
175         try {
176             if (value instanceof Double) {
177                 if (STATE_MIN.equals(stateName)) {
178                     minValue = (Double) value;
179                 } else if (STATE_MAX.equals(stateName)) {
180                     maxValue = (Double) value;
181                 } else if (STATE_STEP.equals(stateName)) {
182                     stepValue = (Double) value;
183                     if (stepValue <= 0) {
184                         logger.warn("Value selector step value <= 0: {}", stepValue);
185                         stepValue = null;
186                     }
187                 } else {
188                     super.onStateChange(state);
189                     return;
190                 }
191             }
192         } catch (NumberFormatException e) {
193             logger.debug("Error parsing value for state {}: {}", stateName, e.getMessage());
194         }
195         if (minValue != null && maxValue != null && stepValue != null && minValue < maxValue) {
196             StateDescriptionFragment fragment = StateDescriptionFragmentBuilder.create()
197                     .withMinimum(new BigDecimal(minValue)).withMaximum(new BigDecimal(maxValue))
198                     .withStep(new BigDecimal(stepValue)).withPattern(format).withReadOnly(false).build();
199             addChannelStateDescriptionFragment(channelId, fragment);
200             addChannelStateDescriptionFragment(numberChannelId, fragment);
201         }
202     }
203 }