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.modbus.e3dc.internal.handler;
15 import static org.openhab.binding.modbus.e3dc.internal.E3DCBindingConstants.*;
16 import static org.openhab.binding.modbus.e3dc.internal.modbus.E3DCModbusConstans.*;
18 import java.util.BitSet;
19 import java.util.Optional;
20 import java.util.OptionalInt;
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.openhab.binding.modbus.e3dc.internal.E3DCWallboxConfiguration;
25 import org.openhab.binding.modbus.e3dc.internal.dto.DataConverter;
26 import org.openhab.binding.modbus.e3dc.internal.dto.WallboxArray;
27 import org.openhab.binding.modbus.e3dc.internal.dto.WallboxBlock;
28 import org.openhab.binding.modbus.e3dc.internal.modbus.Data;
29 import org.openhab.binding.modbus.e3dc.internal.modbus.Data.DataType;
30 import org.openhab.binding.modbus.e3dc.internal.modbus.Parser;
31 import org.openhab.core.io.transport.modbus.AsyncModbusFailure;
32 import org.openhab.core.io.transport.modbus.AsyncModbusReadResult;
33 import org.openhab.core.io.transport.modbus.ModbusCommunicationInterface;
34 import org.openhab.core.io.transport.modbus.ModbusReadRequestBlueprint;
35 import org.openhab.core.io.transport.modbus.ModbusRegisterArray;
36 import org.openhab.core.io.transport.modbus.ModbusWriteRegisterRequestBlueprint;
37 import org.openhab.core.library.types.OnOffType;
38 import org.openhab.core.thing.Bridge;
39 import org.openhab.core.thing.ChannelUID;
40 import org.openhab.core.thing.Thing;
41 import org.openhab.core.thing.ThingStatus;
42 import org.openhab.core.thing.ThingStatusDetail;
43 import org.openhab.core.thing.binding.BaseThingHandler;
44 import org.openhab.core.thing.binding.ThingHandler;
45 import org.openhab.core.types.Command;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
50 * The {@link E3DCWallboxThingHandler} Basic modbus connection towards the E3DC device
52 * @author Bernd Weymann - Initial contribution
55 public class E3DCWallboxThingHandler extends BaseThingHandler {
56 public enum ReadWriteSuccess {
62 private static final String READ_WRITE_ERROR = "Modbus Data Read/Write Error";
63 private static final String READ_ERROR = "Modbus Read Error";
64 private static final String WRITE_ERROR = "Modbus Write Error";
66 ChannelUID wbAvailableChannel;
67 ChannelUID wbSunmodeChannel;
68 ChannelUID wbChargingAbortedChannel;
69 ChannelUID wbChargingChannel;
70 ChannelUID wbJackLockedChannel;
71 ChannelUID wbJackPluggedChannel;
72 ChannelUID wbSchukoOnChannel;
73 ChannelUID wbSchukoPluggedChannel;
74 ChannelUID wbSchukoLockedChannel;
75 ChannelUID wbSchukoRelay16Channel;
76 ChannelUID wbRelay16Channel;
77 ChannelUID wbRelay32Channel;
78 ChannelUID wb1phaseChannel;
80 private final Logger logger = LoggerFactory.getLogger(E3DCWallboxThingHandler.class);
81 private final Parser dataParser = new Parser(DataType.DATA);
82 private ReadWriteSuccess dataRead = ReadWriteSuccess.NOT_RECEIVED;
83 private ReadWriteSuccess dataWrite = ReadWriteSuccess.NOT_RECEIVED;
84 private volatile BitSet currentBitSet = new BitSet(16);
85 private @Nullable E3DCWallboxConfiguration config;
86 private @Nullable E3DCThingHandler bridgeHandler;
88 public E3DCWallboxThingHandler(Thing thing) {
90 wbAvailableChannel = new ChannelUID(thing.getUID(), WB_AVAILABLE_CHANNEL);
91 wbSunmodeChannel = new ChannelUID(thing.getUID(), WB_SUNMODE_CHANNEL);
92 wbChargingAbortedChannel = new ChannelUID(thing.getUID(), WB_CHARGING_ABORTED_CHANNEL);
93 wbChargingChannel = new ChannelUID(thing.getUID(), WB_CHARGING_CHANNEL);
94 wbJackLockedChannel = new ChannelUID(thing.getUID(), WB_JACK_LOCKED_CHANNEL);
95 wbJackPluggedChannel = new ChannelUID(thing.getUID(), WB_JACK_PLUGGED_CHANNEL);
96 wbSchukoOnChannel = new ChannelUID(thing.getUID(), WB_SCHUKO_ON_CHANNEL);
97 wbSchukoPluggedChannel = new ChannelUID(thing.getUID(), WB_SCHUKO_PLUGGED_CHANNEL);
98 wbSchukoLockedChannel = new ChannelUID(thing.getUID(), WB_SCHUKO_LOCKED_CHANNEL);
99 wbSchukoRelay16Channel = new ChannelUID(thing.getUID(), WB_SCHUKO_RELAY_16A_CHANNEL);
100 wbRelay16Channel = new ChannelUID(thing.getUID(), WB_RELAY_16A_CHANNEL);
101 wbRelay32Channel = new ChannelUID(thing.getUID(), WB_RELAY_32A_CHANNEL);
102 wb1phaseChannel = new ChannelUID(thing.getUID(), WB_1PHASE_CHANNEL);
106 public void initialize() {
107 updateStatus(ThingStatus.UNKNOWN);
108 config = getConfigAs(E3DCWallboxConfiguration.class);
109 Bridge bridge = getBridge();
110 if (bridge != null) {
111 ThingHandler handler = bridge.getHandler();
112 if (handler != null) {
113 bridgeHandler = ((E3DCThingHandler) handler);
115 logger.warn("Thing Handler null");
118 logger.warn("Bridge null");
123 public void handleCommand(ChannelUID channelUID, Command command) {
124 if (command instanceof OnOffType) {
126 synchronized (this) {
127 if (channelUID.getIdWithoutGroup().equals(WB_SUNMODE_CHANNEL)) {
128 currentBitSet.set(WB_SUNMODE_BIT, command.equals(OnOffType.ON));
129 } else if (channelUID.getIdWithoutGroup().equals(WB_CHARGING_ABORTED_CHANNEL)) {
130 currentBitSet.set(WB_CHARGING_ABORTED_BIT, command.equals(OnOffType.ON));
131 } else if (channelUID.getIdWithoutGroup().equals(WB_SCHUKO_ON_CHANNEL)) {
132 currentBitSet.set(WB_SCHUKO_ON_BIT, command.equals(OnOffType.ON));
133 } else if (channelUID.getIdWithoutGroup().equals(WB_1PHASE_CHANNEL)) {
134 currentBitSet.set(WB_1PHASE_BIT, command.equals(OnOffType.ON));
136 writeValue = DataConverter.toInt(currentBitSet);
137 logger.debug("Wallbox write {}", writeValue);
139 OptionalInt wallboxId = getWallboxId(config);
140 if (wallboxId.isPresent()) {
141 wallboxSet(wallboxId.getAsInt(), writeValue);
147 * Wallbox Settings can be changed with one Integer
149 * @param wallboxId needed to calculate right register
150 * @param writeValue integer to be written
152 public void wallboxSet(int wallboxId, int writeValue) {
153 E3DCThingHandler localBridgeHandler = bridgeHandler;
154 if (localBridgeHandler != null) {
155 ModbusCommunicationInterface comms = localBridgeHandler.getComms();
157 ModbusRegisterArray regArray = new ModbusRegisterArray(writeValue);
158 ModbusWriteRegisterRequestBlueprint writeBluePrint = new ModbusWriteRegisterRequestBlueprint(
159 localBridgeHandler.getSlaveId(), WALLBOX_REG_START + wallboxId, regArray, false, 3);
160 comms.submitOneTimeWrite(writeBluePrint, result -> {
161 if (dataWrite != ReadWriteSuccess.SUCCESS) {
162 dataWrite = ReadWriteSuccess.SUCCESS;
165 logger.debug("E3DC Modbus write response! {}", result.getResponse().toString());
167 if (dataWrite != ReadWriteSuccess.FAILED) {
168 dataWrite = ReadWriteSuccess.FAILED;
171 logger.warn("E3DC Modbus write error! {}", failure.getRequest().toString());
177 private OptionalInt getWallboxId(@Nullable E3DCWallboxConfiguration c) {
179 return OptionalInt.of(c.wallboxId);
181 return OptionalInt.empty();
185 public void handle(AsyncModbusReadResult result) {
186 if (dataRead != ReadWriteSuccess.SUCCESS) {
187 dataRead = ReadWriteSuccess.SUCCESS;
190 dataParser.handle(result);
191 Optional<Data> wbArrayOpt = dataParser.parse(DataType.WALLBOX);
192 if (wbArrayOpt.isPresent()) {
193 WallboxArray wbArray = (WallboxArray) wbArrayOpt.get();
194 OptionalInt wallboxId = getWallboxId(config);
195 if (wallboxId.isPresent()) {
196 Optional<WallboxBlock> blockOpt = wbArray.getWallboxBlock(wallboxId.getAsInt());
197 if (blockOpt.isPresent()) {
198 WallboxBlock block = blockOpt.get();
199 synchronized (this) {
200 currentBitSet = block.getBitSet();
202 updateState(wbAvailableChannel, block.wbAvailable);
203 updateState(wbSunmodeChannel, block.wbSunmode);
204 updateState(wbChargingAbortedChannel, block.wbChargingAborted);
205 updateState(wbChargingChannel, block.wbCharging);
206 updateState(wbJackLockedChannel, block.wbJackLocked);
207 updateState(wbJackPluggedChannel, block.wbJackPlugged);
208 updateState(wbSchukoOnChannel, block.wbSchukoOn);
209 updateState(wbSchukoPluggedChannel, block.wbSchukoPlugged);
210 updateState(wbSchukoLockedChannel, block.wbSchukoLocked);
211 updateState(wbSchukoRelay16Channel, block.wbSchukoRelay16);
212 updateState(wbRelay16Channel, block.wbRelay16);
213 updateState(wbRelay32Channel, block.wbRelay32);
214 updateState(wb1phaseChannel, block.wb1phase);
216 logger.debug("Unable to get ID {} from WallboxArray", wallboxId);
219 logger.debug("Wallbox ID {} not valid", wallboxId);
222 logger.debug("Unable to get {} from Bridge", DataType.WALLBOX);
226 public void handleError(AsyncModbusFailure<ModbusReadRequestBlueprint> result) {
227 if (dataRead != ReadWriteSuccess.FAILED) {
228 dataRead = ReadWriteSuccess.FAILED;
233 private void updateStatus() {
234 if (dataWrite == ReadWriteSuccess.NOT_RECEIVED) {
235 // read success / write not happened yet => go online / offline
236 if (dataRead == ReadWriteSuccess.SUCCESS) {
237 updateStatus(ThingStatus.ONLINE);
239 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, READ_ERROR);
242 if (dataRead == dataWrite) {
243 // read and write same status - either go online or offline
244 if (dataRead == ReadWriteSuccess.SUCCESS) {
245 updateStatus(ThingStatus.ONLINE);
247 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, READ_WRITE_ERROR);
250 // either read or write failed - go offline with detailed status
251 if (dataRead == ReadWriteSuccess.SUCCESS) {
252 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, WRITE_ERROR);
254 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, READ_ERROR);