2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.echonetlite.internal;
15 import static org.openhab.binding.echonetlite.internal.HexUtil.hex;
16 import static org.openhab.binding.echonetlite.internal.LangUtil.b;
18 import java.nio.ByteBuffer;
19 import java.nio.ByteOrder;
20 import java.util.HashMap;
22 import java.util.Objects;
23 import java.util.concurrent.TimeUnit;
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;
38 * @author Michael Barker - Initial contribution
41 public interface StateCodec extends StateEncode, StateDecode {
43 class OnOffCodec implements StateCodec {
45 private final int off;
47 public OnOffCodec(int on, int off) {
53 public State decodeState(final ByteBuffer edt) {
54 return b(on) == edt.get() ? OnOffType.ON : OnOffType.OFF;
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));
64 public String itemType() {
69 enum StandardVersionInformationCodec implements StateDecode {
74 public State decodeState(final ByteBuffer edt) {
75 final int pdc = edt.remaining();
77 return StringType.EMPTY;
80 return new StringType("" + (char) edt.get(edt.position() + 2));
84 public String itemType() {
89 enum HexStringCodec implements StateDecode {
94 public State decodeState(final ByteBuffer edt) {
95 return new StringType(hex(edt, "", "", "", ""));
99 public String itemType() {
104 enum OperatingTimeDecode implements StateDecode {
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);
112 final int b0 = edt.get() & 0xFF;
113 final long time = edt.getInt() & 0xFFFFFFFFL;
115 final TimeUnit timeUnit;
118 timeUnit = TimeUnit.MINUTES;
122 timeUnit = TimeUnit.HOURS;
126 timeUnit = TimeUnit.DAYS;
131 timeUnit = TimeUnit.SECONDS;
135 return new QuantityType<>(timeUnit.toSeconds(time), Units.SECOND);
139 public String itemType() {
140 return "Number:Time";
147 final StringType state;
149 public Option(final String name, final int value) {
152 this.state = new StringType(name);
156 class OptionCodec implements StateCodec {
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");
163 public OptionCodec(Option... options) {
164 for (Option option : options) {
165 optionByName.put(option.name, option);
166 optionByValue[option.value] = option;
171 public String itemType() {
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;
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));
188 logger.warn("No option specified for: {}", state);
193 enum Decimal8bitCodec implements StateCodec {
198 public String itemType() {
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);
209 public void encodeState(final State state, final ByteBuffer edt) {
210 edt.put((byte) (((DecimalType) state).intValue()));
214 enum Temperature8bitCodec implements StateCodec {
218 public State decodeState(final ByteBuffer edt) {
219 final int value = edt.get();
220 return new QuantityType<>(value, SIUnits.CELSIUS);
224 public String itemType() {
225 return "Number:Temperature";
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()));