]> git.basschouten.com Git - openhab-addons.git/blob
44f857d13a9aa3224fb6fdd054be3d51c2149712
[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.echonetlite.internal;
14
15 import static org.openhab.binding.echonetlite.internal.HexUtil.hex;
16 import static org.openhab.binding.echonetlite.internal.LangUtil.b;
17
18 import java.nio.ByteBuffer;
19 import java.nio.ByteOrder;
20 import java.util.HashMap;
21 import java.util.Map;
22 import java.util.Objects;
23 import java.util.concurrent.TimeUnit;
24
25 import org.eclipse.jdt.annotation.NonNullByDefault;
26 import org.eclipse.jdt.annotation.Nullable;
27 import org.openhab.core.library.types.DecimalType;
28 import org.openhab.core.library.types.OnOffType;
29 import org.openhab.core.library.types.QuantityType;
30 import org.openhab.core.library.types.StringType;
31 import org.openhab.core.library.unit.SIUnits;
32 import org.openhab.core.library.unit.Units;
33 import org.openhab.core.types.State;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37 /**
38  * @author Michael Barker - Initial contribution
39  */
40 @NonNullByDefault
41 public interface StateCodec extends StateEncode, StateDecode {
42
43     class OnOffCodec implements StateCodec {
44         private final int on;
45         private final int off;
46
47         public OnOffCodec(int on, int off) {
48             this.on = on;
49             this.off = off;
50         }
51
52         @Override
53         public State decodeState(final ByteBuffer edt) {
54             return b(on) == edt.get() ? OnOffType.ON : OnOffType.OFF;
55         }
56
57         @Override
58         public void encodeState(final State state, final ByteBuffer edt) {
59             final OnOffType onOff = (OnOffType) state;
60             edt.put(onOff == OnOffType.ON ? b(on) : b(off));
61         }
62
63         @Override
64         public String itemType() {
65             return "Switch";
66         }
67     }
68
69     enum StandardVersionInformationCodec implements StateDecode {
70
71         INSTANCE;
72
73         @Override
74         public State decodeState(final ByteBuffer edt) {
75             final int pdc = edt.remaining();
76             if (pdc != 4) {
77                 return StringType.EMPTY;
78             }
79
80             return new StringType("" + (char) edt.get(edt.position() + 2));
81         }
82
83         @Override
84         public String itemType() {
85             return "String";
86         }
87     }
88
89     enum HexStringCodec implements StateDecode {
90
91         INSTANCE;
92
93         @Override
94         public State decodeState(final ByteBuffer edt) {
95             return new StringType(hex(edt, "", "", "", ""));
96         }
97
98         @Override
99         public String itemType() {
100             return "String";
101         }
102     }
103
104     enum OperatingTimeDecode implements StateDecode {
105         INSTANCE;
106
107         @Override
108         public State decodeState(final ByteBuffer edt) {
109             // Specification isn't explicit about byte order, but seems to be work with testing.
110             edt.order(ByteOrder.BIG_ENDIAN);
111
112             final int b0 = edt.get() & 0xFF;
113             final long time = edt.getInt() & 0xFFFFFFFFL;
114
115             final TimeUnit timeUnit;
116             switch (b0) {
117                 case 0x42:
118                     timeUnit = TimeUnit.MINUTES;
119                     break;
120
121                 case 0x43:
122                     timeUnit = TimeUnit.HOURS;
123                     break;
124
125                 case 0x44:
126                     timeUnit = TimeUnit.DAYS;
127                     break;
128
129                 case 0x41:
130                 default:
131                     timeUnit = TimeUnit.SECONDS;
132                     break;
133             }
134
135             return new QuantityType<>(timeUnit.toSeconds(time), Units.SECOND);
136         }
137
138         @Override
139         public String itemType() {
140             return "Number:Time";
141         }
142     }
143
144     class Option {
145         final String name;
146         final int value;
147         final StringType state;
148
149         public Option(final String name, final int value) {
150             this.name = name;
151             this.value = value;
152             this.state = new StringType(name);
153         }
154     }
155
156     class OptionCodec implements StateCodec {
157
158         private final Logger logger = LoggerFactory.getLogger(OptionCodec.class);
159         private final Map<String, Option> optionByName = new HashMap<>();
160         private final Option[] optionByValue = new Option[256]; // All options values are single bytes on the wire
161         private final StringType unknown = new StringType("Unknown");
162
163         public OptionCodec(Option... options) {
164             for (Option option : options) {
165                 optionByName.put(option.name, option);
166                 optionByValue[option.value] = option;
167             }
168         }
169
170         @Override
171         public String itemType() {
172             return "String";
173         }
174
175         @Override
176         public State decodeState(final ByteBuffer edt) {
177             final int value = edt.get() & 0xFF;
178             final Option option = optionByValue[value];
179             return null != option ? option.state : unknown;
180         }
181
182         @Override
183         public void encodeState(final State state, final ByteBuffer edt) {
184             final Option option = optionByName.get(state.toFullString());
185             if (null != option) {
186                 edt.put(b(option.value));
187             } else {
188                 logger.warn("No option specified for: {}", state);
189             }
190         }
191     }
192
193     enum Decimal8bitCodec implements StateCodec {
194
195         INSTANCE;
196
197         @Override
198         public String itemType() {
199             return "Number";
200         }
201
202         @Override
203         public State decodeState(final ByteBuffer edt) {
204             final int value = edt.get(); // Should expand to typed value (mask excluded)
205             return new DecimalType(value);
206         }
207
208         @Override
209         public void encodeState(final State state, final ByteBuffer edt) {
210             edt.put((byte) (((DecimalType) state).intValue()));
211         }
212     }
213
214     enum Temperature8bitCodec implements StateCodec {
215         INSTANCE;
216
217         @Override
218         public State decodeState(final ByteBuffer edt) {
219             final int value = edt.get();
220             return new QuantityType<>(value, SIUnits.CELSIUS);
221         }
222
223         @Override
224         public String itemType() {
225             return "Number:Temperature";
226         }
227
228         @Override
229         public void encodeState(final State state, final ByteBuffer edt) {
230             final @Nullable QuantityType<?> tempCelsius = ((QuantityType<?>) state).toUnit(SIUnits.CELSIUS);
231             edt.put((byte) (Objects.requireNonNull(tempCelsius).intValue()));
232         }
233     }
234 }