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) {
52 public State decodeState(final ByteBuffer edt) {
53 return b(on) == edt.get() ? OnOffType.ON : OnOffType.OFF;
56 public void encodeState(final State state, final ByteBuffer edt) {
57 final OnOffType onOff = (OnOffType) state;
58 edt.put(onOff == OnOffType.ON ? b(on) : b(off));
61 public String itemType() {
66 enum StandardVersionInformationCodec implements StateDecode {
70 public State decodeState(final ByteBuffer edt) {
71 final int pdc = edt.remaining();
73 return StringType.EMPTY;
76 return new StringType("" + (char) edt.get(edt.position() + 2));
79 public String itemType() {
84 enum HexStringCodec implements StateDecode {
88 public State decodeState(final ByteBuffer edt) {
89 return new StringType(hex(edt, "", "", "", ""));
92 public String itemType() {
97 enum OperatingTimeDecode implements StateDecode {
100 public State decodeState(final ByteBuffer edt) {
101 // Specification isn't explicit about byte order, but seems to be work with testing.
102 edt.order(ByteOrder.BIG_ENDIAN);
104 final int b0 = edt.get() & 0xFF;
105 final long time = edt.getInt() & 0xFFFFFFFFL;
107 final TimeUnit timeUnit;
110 timeUnit = TimeUnit.MINUTES;
114 timeUnit = TimeUnit.HOURS;
118 timeUnit = TimeUnit.DAYS;
123 timeUnit = TimeUnit.SECONDS;
127 return new QuantityType<>(timeUnit.toSeconds(time), Units.SECOND);
130 public String itemType() {
131 return "Number:Time";
138 final StringType state;
140 public Option(final String name, final int value) {
143 this.state = new StringType(name);
147 class OptionCodec implements StateCodec {
149 private final Logger logger = LoggerFactory.getLogger(OptionCodec.class);
150 private final Map<String, Option> optionByName = new HashMap<>();
151 private final Option[] optionByValue = new Option[256]; // All options values are single bytes on the wire
152 private final StringType unknown = new StringType("Unknown");
154 public OptionCodec(Option... options) {
155 for (Option option : options) {
156 optionByName.put(option.name, option);
157 optionByValue[option.value] = option;
161 public String itemType() {
165 public State decodeState(final ByteBuffer edt) {
166 final int value = edt.get() & 0xFF;
167 final Option option = optionByValue[value];
168 return null != option ? option.state : unknown;
171 public void encodeState(final State state, final ByteBuffer edt) {
172 final Option option = optionByName.get(state.toFullString());
173 if (null != option) {
174 edt.put(b(option.value));
176 logger.warn("No option specified for: {}", state);
181 enum Decimal8bitCodec implements StateCodec {
185 public String itemType() {
189 public State decodeState(final ByteBuffer edt) {
190 final int value = edt.get(); // Should expand to typed value (mask excluded)
191 return new DecimalType(value);
194 public void encodeState(final State state, final ByteBuffer edt) {
195 edt.put((byte) (((DecimalType) state).intValue()));
199 enum Temperature8bitCodec implements StateCodec {
202 public State decodeState(final ByteBuffer edt) {
203 final int value = edt.get();
204 return new QuantityType<>(value, SIUnits.CELSIUS);
207 public String itemType() {
208 return "Number:Temperature";
211 public void encodeState(final State state, final ByteBuffer edt) {
212 final @Nullable QuantityType<?> tempCelsius = ((QuantityType<?>) state).toUnit(SIUnits.CELSIUS);
213 edt.put((byte) (Objects.requireNonNull(tempCelsius).intValue()));