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