]> git.basschouten.com Git - openhab-addons.git/blob
4c33ae02337ecee8d0eb04a115f71afdc23514d7
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 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.homematic.internal.communicator.virtual;
14
15 import static org.openhab.binding.homematic.internal.misc.HomematicConstants.*;
16
17 import java.io.IOException;
18 import java.nio.charset.StandardCharsets;
19 import java.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.HashMap;
22 import java.util.List;
23 import java.util.Map;
24
25 import org.apache.commons.lang.ObjectUtils;
26 import org.apache.commons.lang.StringUtils;
27 import org.openhab.binding.homematic.internal.misc.HomematicClientException;
28 import org.openhab.binding.homematic.internal.misc.MiscUtils;
29 import org.openhab.binding.homematic.internal.model.HmChannel;
30 import org.openhab.binding.homematic.internal.model.HmDatapoint;
31 import org.openhab.binding.homematic.internal.model.HmDatapointConfig;
32 import org.openhab.binding.homematic.internal.model.HmDatapointInfo;
33 import org.openhab.binding.homematic.internal.model.HmDevice;
34 import org.openhab.binding.homematic.internal.model.HmParamsetType;
35 import org.openhab.binding.homematic.internal.model.HmValueType;
36
37 /**
38  * The {@link DisplayTextVirtualDatapoint} adds multiple virtual datapoints to the HM-Dis-WM55 and HM-Dis-EP-WM55
39  * devices to easily handle colored text, icons, the led and the beeper of the display.
40  *
41  * @author Gerhard Riegler - Initial contribution
42  */
43
44 public class DisplayTextVirtualDatapoint extends AbstractVirtualDatapointHandler {
45
46     private static final String DATAPOINT_NAME_DISPLAY_LINE = "DISPLAY_LINE_";
47     private static final String DATAPOINT_NAME_DISPLAY_COLOR = "DISPLAY_COLOR_";
48     private static final String DATAPOINT_NAME_DISPLAY_ICON = "DISPLAY_ICON_";
49     private static final String DATAPOINT_NAME_DISPLAY_LED = "DISPLAY_LED";
50     private static final String DATAPOINT_NAME_DISPLAY_BEEPER = "DISPLAY_BEEPER";
51     private static final String DATAPOINT_NAME_DISPLAY_BEEPCOUNT = "DISPLAY_BEEPCOUNT";
52     private static final String DATAPOINT_NAME_DISPLAY_BEEPINTERVAL = "DISPLAY_BEEPINTERVAL";
53     private static final String DATAPOINT_NAME_DISPLAY_SUBMIT = "DISPLAY_SUBMIT";
54
55     private static final String START = "0x02";
56     private static final String STOP = "0x03";
57     private static final String LINE = "0x12";
58     private static final String COLOR = "0x11";
59     private static final String LF = "0x0a";
60     private static final String ICON = "0x13";
61
62     private static final String BEEPER_START = "0x14";
63     private static final String BEEPER_END = "0x1c";
64     private static final String BEEPCOUNT_END = "0x1D";
65     private static final String BEEPINTERVAL_END = "0x16";
66
67     private static Map<String, String> replaceMap = new HashMap<>();
68
69     // replace special chars while encoding
70     static {
71         replaceMap.put("d6", "23");
72         replaceMap.put("dc", "24");
73         replaceMap.put("3d", "27");
74         replaceMap.put("c4", "5b");
75         replaceMap.put("df", "5f");
76         replaceMap.put("e4", "7b");
77         replaceMap.put("f6", "7c");
78         replaceMap.put("fc", "7d");
79     }
80
81     /**
82      * Available text colors.
83      */
84     private enum Color {
85         NONE(""),
86         WHITE("0x80"),
87         RED("0x81"),
88         ORANGE("0x82"),
89         YELLOW("0x83"),
90         GREEN("0x84"),
91         BLUE("0x85");
92
93         private final String code;
94
95         private Color(String code) {
96             this.code = code;
97         }
98
99         protected String getCode() {
100             return code;
101         }
102
103         /**
104          * Returns the color code.
105          */
106         public static String getCode(String name) {
107             try {
108                 return valueOf(name).getCode();
109             } catch (Exception ex) {
110                 return null;
111             }
112         }
113     }
114
115     /**
116      * Available icons.
117      */
118     private enum Icon {
119         NONE(""),
120         OFF("0x80"),
121         ON("0x81"),
122         OPEN("0x82"),
123         CLOSED("0x83"),
124         ERROR("0x84"),
125         OK("0x85"),
126         INFO("0x86"),
127         NEW_MESSAGE("0x87"),
128         SERVICE("0x88"),
129         SIGNAL_GREEN("0x89"),
130         SIGNAL_YELLOW("0x8a"),
131         SIGNAL_RED("0x8b");
132
133         private final String code;
134
135         private Icon(String code) {
136             this.code = code;
137         }
138
139         protected String getCode() {
140             return code;
141         }
142
143         /**
144          * Returns the icon code.
145          */
146         public static String getCode(String name) {
147             try {
148                 return valueOf(name).getCode();
149             } catch (Exception ex) {
150                 return null;
151             }
152         }
153     }
154
155     /**
156      * Available Beeper codes.
157      */
158     private enum Beeper {
159         OFF("0xc0"),
160         LONG_LONG("0xc1"),
161         LONG_SHORT("0xc2"),
162         LONG_SHORT_SHORT("0xc3"),
163         SHORT("0xc4"),
164         SHORT_SHORT("0xc5"),
165         LONG("0xc6");
166
167         private final String code;
168
169         private Beeper(String code) {
170             this.code = code;
171         }
172
173         protected String getCode() {
174             return code;
175         }
176
177         /**
178          * Returns the beeper code.
179          */
180         public static String getCode(String name) {
181             try {
182                 return valueOf(name).getCode();
183             } catch (Exception ex) {
184                 return OFF.getCode();
185             }
186         }
187     }
188
189     /**
190      * Available LED colors.
191      */
192     private enum Led {
193         OFF("0xf0"),
194         RED("0xf1"),
195         GREEN("0xf2"),
196         ORANGE("0xf3");
197
198         private final String code;
199
200         private Led(String code) {
201             this.code = code;
202         }
203
204         protected String getCode() {
205             return code;
206         }
207
208         /**
209          * Returns the LED code.
210          */
211         public static String getCode(String name) {
212             try {
213                 return valueOf(name).getCode();
214             } catch (Exception ex) {
215                 return OFF.getCode();
216             }
217         }
218     }
219
220     @Override
221     public String getName() {
222         return DATAPOINT_NAME_DISPLAY_SUBMIT;
223     }
224
225     @Override
226     public void initialize(HmDevice device) {
227         if (isDisplay(device)) {
228             for (HmChannel channel : device.getChannels()) {
229                 if (channel.hasDatapoint(new HmDatapointInfo(HmParamsetType.VALUES, channel, DATAPOINT_NAME_SUBMIT))) {
230                     for (int i = 1; i <= getLineCount(device); i++) {
231                         addDatapoint(device, channel.getNumber(), DATAPOINT_NAME_DISPLAY_LINE + i, HmValueType.STRING,
232                                 null, false);
233
234                         addEnumDisplayDatapoint(device, channel.getNumber(), DATAPOINT_NAME_DISPLAY_ICON + i,
235                                 Icon.class);
236
237                         if (!isEpDisplay(device)) {
238                             addEnumDisplayDatapoint(device, channel.getNumber(), DATAPOINT_NAME_DISPLAY_COLOR + i,
239                                     Color.class);
240                         }
241                     }
242                     if (isEpDisplay(device)) {
243                         addEnumDisplayDatapoint(device, channel.getNumber(), DATAPOINT_NAME_DISPLAY_BEEPER,
244                                 Beeper.class);
245                         HmDatapoint bc = addDatapoint(device, channel.getNumber(), DATAPOINT_NAME_DISPLAY_BEEPCOUNT,
246                                 HmValueType.INTEGER, 1, false);
247                         bc.setMinValue(0);
248                         bc.setMaxValue(15);
249                         HmDatapoint bd = addDatapoint(device, channel.getNumber(), DATAPOINT_NAME_DISPLAY_BEEPINTERVAL,
250                                 HmValueType.INTEGER, 1, false);
251                         bd.setMinValue(10);
252                         bd.setMaxValue(160);
253                         bd.setStep(10);
254                         addEnumDisplayDatapoint(device, channel.getNumber(), DATAPOINT_NAME_DISPLAY_LED, Led.class);
255                     }
256                     addDatapoint(device, channel.getNumber(), DATAPOINT_NAME_DISPLAY_SUBMIT, HmValueType.BOOL, false,
257                             false);
258                 }
259             }
260         }
261     }
262
263     /**
264      * Adds a Datapoint to the device with the values of the given enum.
265      */
266     private void addEnumDisplayDatapoint(HmDevice device, int channelNumber, String datapointName,
267             Class<? extends Enum<?>> e) {
268         HmDatapoint dpEnum = addDatapoint(device, channelNumber, datapointName, HmValueType.ENUM, null, false);
269         dpEnum.setOptions(getEnumNames(e));
270         dpEnum.setMinValue(0);
271         dpEnum.setMaxValue(e.getEnumConstants().length);
272     }
273
274     /**
275      * Returns a string array with all the constants in the Enum.
276      */
277     private String[] getEnumNames(Class<? extends Enum<?>> e) {
278         return Arrays.toString(e.getEnumConstants()).replaceAll("^.|.$", "").split(", ");
279     }
280
281     /**
282      * Returns the number of lines of the display.
283      */
284     private int getLineCount(HmDevice device) {
285         return (DEVICE_TYPE_STATUS_DISPLAY.equals(device.getType()) ? 6 : 3);
286     }
287
288     /**
289      * Returns true, if the display is a EP display.
290      */
291     private boolean isEpDisplay(HmDevice device) {
292         return DEVICE_TYPE_EP_STATUS_DISPLAY.equals(device.getType());
293     }
294
295     /**
296      * Returns true, if the device is a supported display.
297      */
298     private boolean isDisplay(HmDevice device) {
299         return device.getType().equals(DEVICE_TYPE_STATUS_DISPLAY) || isEpDisplay(device);
300     }
301
302     @Override
303     public boolean canHandleCommand(HmDatapoint dp, Object value) {
304         HmDevice device = dp.getChannel().getDevice();
305         return (device.getType().equals(DEVICE_TYPE_STATUS_DISPLAY) || isEpDisplay(device))
306                 && (getName().equals(dp.getName()) || dp.getName().startsWith(DATAPOINT_NAME_DISPLAY_LINE)
307                         || dp.getName().startsWith(DATAPOINT_NAME_DISPLAY_COLOR)
308                         || dp.getName().startsWith(DATAPOINT_NAME_DISPLAY_ICON)
309                         || dp.getName().equals(DATAPOINT_NAME_DISPLAY_LED)
310                         || dp.getName().equals(DATAPOINT_NAME_DISPLAY_BEEPER)
311                         || dp.getName().equals(DATAPOINT_NAME_DISPLAY_BEEPCOUNT)
312                         || dp.getName().equals(DATAPOINT_NAME_DISPLAY_BEEPINTERVAL));
313     }
314
315     @Override
316     public void handleCommand(VirtualGateway gateway, HmDatapoint dp, HmDatapointConfig dpConfig, Object value)
317             throws IOException, HomematicClientException {
318         dp.setValue(value);
319
320         if (DATAPOINT_NAME_DISPLAY_SUBMIT.equals(dp.getName()) && MiscUtils.isTrueValue(dp.getValue())) {
321             HmChannel channel = dp.getChannel();
322             boolean isEp = isEpDisplay(channel.getDevice());
323
324             List<String> message = new ArrayList<>();
325             message.add(START);
326             if (isEp) {
327                 message.add(LF);
328             }
329
330             for (int i = 1; i <= getLineCount(channel.getDevice()); i++) {
331                 String line = ObjectUtils.toString(
332                         channel.getDatapoint(HmParamsetType.VALUES, DATAPOINT_NAME_DISPLAY_LINE + i).getValue());
333                 if (StringUtils.isEmpty(line)) {
334                     line = " ";
335                 }
336                 message.add(LINE);
337                 message.add(encodeText(line));
338                 if (!isEp) {
339                     String color = channel.getDatapoint(HmParamsetType.VALUES, DATAPOINT_NAME_DISPLAY_COLOR + i)
340                             .getOptionValue();
341                     message.add(COLOR);
342                     String colorCode = Color.getCode(color);
343                     message.add(StringUtils.isBlank(colorCode) ? Color.WHITE.getCode() : colorCode);
344                 }
345                 String icon = channel.getDatapoint(HmParamsetType.VALUES, DATAPOINT_NAME_DISPLAY_ICON + i)
346                         .getOptionValue();
347                 String iconCode = Icon.getCode(icon);
348                 if (StringUtils.isNotBlank(iconCode)) {
349                     message.add(ICON);
350                     message.add(iconCode);
351                 }
352                 message.add(LF);
353             }
354
355             if (isEp) {
356                 String beeper = channel.getDatapoint(HmParamsetType.VALUES, DATAPOINT_NAME_DISPLAY_BEEPER)
357                         .getOptionValue();
358                 message.add(BEEPER_START);
359                 message.add(Beeper.getCode(beeper));
360                 message.add(BEEPER_END);
361                 // set number of beeps
362                 message.add(
363                         encodeBeepCount(channel.getDatapoint(HmParamsetType.VALUES, DATAPOINT_NAME_DISPLAY_BEEPCOUNT)));
364                 message.add(BEEPCOUNT_END);
365                 // set interval between two beeps
366                 message.add(encodeBeepInterval(
367                         channel.getDatapoint(HmParamsetType.VALUES, DATAPOINT_NAME_DISPLAY_BEEPINTERVAL)));
368                 message.add(BEEPINTERVAL_END);
369                 // LED value must always set (same as beeps)
370                 String led = channel.getDatapoint(HmParamsetType.VALUES, DATAPOINT_NAME_DISPLAY_LED).getOptionValue();
371                 message.add(Led.getCode(led));
372
373             }
374             message.add(STOP);
375
376             gateway.sendDatapoint(channel.getDatapoint(HmParamsetType.VALUES, DATAPOINT_NAME_SUBMIT),
377                     new HmDatapointConfig(), StringUtils.join(message, ","), null);
378         }
379     }
380
381     /**
382      * Encodes the beep count value. Allowed values 0 - 15, where 0 means infinite.
383      */
384     private String encodeBeepCount(HmDatapoint dp) {
385         int counts = (int) (Number) dp.getValue();
386         if (counts == 0) {
387             counts = 16;
388         }
389         return String.format("0x%02x", 207 + counts);
390     }
391
392     /**
393      * Encodes the beep interval value in 10 s steps. Allowed values 10 - 160.
394      */
395     private String encodeBeepInterval(HmDatapoint dp) {
396         int interval = (int) (Number) dp.getValue();
397         return String.format("0x%02x", 224 + ((interval - 1) / 10));
398     }
399
400     /**
401      * Encodes the given text for the display.
402      */
403     private String encodeText(String text) {
404         final byte[] bytes = text.getBytes(StandardCharsets.ISO_8859_1);
405         StringBuilder sb = new StringBuilder(bytes.length * 2);
406         for (byte b : bytes) {
407             sb.append("0x");
408             String hexValue = String.format("%02x", b);
409             sb.append(replaceMap.containsKey(hexValue) ? replaceMap.get(hexValue) : hexValue);
410             sb.append(",");
411         }
412         sb.setLength(sb.length() - 1);
413         return sb.toString();
414     }
415 }