2 * Copyright (c) 2010-2020 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.stiebeleltron.internal.handler;
15 import static org.openhab.binding.modbus.stiebeleltron.internal.StiebelEltronBindingConstants.*;
16 import static org.openhab.core.library.unit.SIUnits.CELSIUS;
17 import static org.openhab.core.library.unit.SmartHomeUnits.KILOWATT_HOUR;
18 import static org.openhab.core.library.unit.SmartHomeUnits.PERCENT;
20 import java.util.Optional;
22 import javax.measure.Unit;
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.openhab.binding.modbus.handler.EndpointNotInitializedException;
27 import org.openhab.binding.modbus.handler.ModbusEndpointThingHandler;
28 import org.openhab.binding.modbus.stiebeleltron.internal.StiebelEltronConfiguration;
29 import org.openhab.binding.modbus.stiebeleltron.internal.dto.EnergyBlock;
30 import org.openhab.binding.modbus.stiebeleltron.internal.dto.SystemInformationBlock;
31 import org.openhab.binding.modbus.stiebeleltron.internal.dto.SystemParameterBlock;
32 import org.openhab.binding.modbus.stiebeleltron.internal.dto.SystemStateBlock;
33 import org.openhab.binding.modbus.stiebeleltron.internal.parser.EnergyBlockParser;
34 import org.openhab.binding.modbus.stiebeleltron.internal.parser.SystemInfromationBlockParser;
35 import org.openhab.binding.modbus.stiebeleltron.internal.parser.SystemParameterBlockParser;
36 import org.openhab.binding.modbus.stiebeleltron.internal.parser.SystemStateBlockParser;
37 import org.openhab.core.library.types.DecimalType;
38 import org.openhab.core.library.types.OpenClosedType;
39 import org.openhab.core.library.types.QuantityType;
40 import org.openhab.core.thing.Bridge;
41 import org.openhab.core.thing.ChannelUID;
42 import org.openhab.core.thing.Thing;
43 import org.openhab.core.thing.ThingStatus;
44 import org.openhab.core.thing.ThingStatusDetail;
45 import org.openhab.core.thing.ThingStatusInfo;
46 import org.openhab.core.thing.binding.BaseThingHandler;
47 import org.openhab.core.thing.binding.ThingHandler;
48 import org.openhab.core.types.Command;
49 import org.openhab.core.types.RefreshType;
50 import org.openhab.core.types.State;
51 import org.openhab.io.transport.modbus.AsyncModbusFailure;
52 import org.openhab.io.transport.modbus.ModbusCommunicationInterface;
53 import org.openhab.io.transport.modbus.ModbusReadFunctionCode;
54 import org.openhab.io.transport.modbus.ModbusReadRequestBlueprint;
55 import org.openhab.io.transport.modbus.ModbusRegister;
56 import org.openhab.io.transport.modbus.ModbusRegisterArray;
57 import org.openhab.io.transport.modbus.ModbusWriteRegisterRequestBlueprint;
58 import org.openhab.io.transport.modbus.ModbusWriteRequestBlueprint;
59 import org.openhab.io.transport.modbus.PollTask;
60 import org.slf4j.Logger;
61 import org.slf4j.LoggerFactory;
64 * The {@link Modbus.StiebelEltronHandler} is responsible for handling commands,
65 * which are sent to one of the channels and for polling the modbus.
67 * @author Paul Frank - Initial contribution
70 public class StiebelEltronHandler extends BaseThingHandler {
72 public abstract class AbstractBasePoller {
74 private final Logger logger = LoggerFactory.getLogger(StiebelEltronHandler.class);
76 private volatile @Nullable PollTask pollTask;
78 public synchronized void unregisterPollTask() {
79 PollTask task = pollTask;
84 ModbusCommunicationInterface mycomms = StiebelEltronHandler.this.comms;
85 if (mycomms != null) {
86 mycomms.unregisterRegularPoll(task);
92 * Register poll task This is where we set up our regular poller
94 public synchronized void registerPollTask(int address, int length, ModbusReadFunctionCode readFunctionCode) {
96 logger.debug("Setting up regular polling");
98 ModbusCommunicationInterface mycomms = StiebelEltronHandler.this.comms;
99 StiebelEltronConfiguration myconfig = StiebelEltronHandler.this.config;
100 if (myconfig == null || mycomms == null) {
101 throw new IllegalStateException("registerPollTask called without proper configuration");
104 ModbusReadRequestBlueprint request = new ModbusReadRequestBlueprint(getSlaveId(), readFunctionCode, address,
105 length, myconfig.getMaxTries());
107 long refreshMillis = myconfig.getRefreshMillis();
109 pollTask = mycomms.registerRegularPoll(request, refreshMillis, 1000, result -> {
110 result.getRegisters().ifPresent(this::handlePolledData);
111 if (getThing().getStatus() != ThingStatus.ONLINE) {
112 updateStatus(ThingStatus.ONLINE);
114 }, StiebelEltronHandler.this::handleReadError);
117 public synchronized void poll() {
118 PollTask task = pollTask;
119 ModbusCommunicationInterface mycomms = StiebelEltronHandler.this.comms;
120 if (task != null && mycomms != null) {
121 mycomms.submitOneTimePoll(task.getRequest(), task.getResultCallback(), task.getFailureCallback());
125 protected abstract void handlePolledData(ModbusRegisterArray registers);
131 private final Logger logger = LoggerFactory.getLogger(StiebelEltronHandler.class);
134 * Configuration instance
136 protected @Nullable StiebelEltronConfiguration config = null;
138 * Parser used to convert incoming raw messages into system blocks
140 private final SystemInfromationBlockParser systemInformationBlockParser = new SystemInfromationBlockParser();
142 * Parser used to convert incoming raw messages into system state blocks
144 private final SystemStateBlockParser systemstateBlockParser = new SystemStateBlockParser();
146 * Parser used to convert incoming raw messages into system parameter blocks
148 private final SystemParameterBlockParser systemParameterBlockParser = new SystemParameterBlockParser();
150 * Parser used to convert incoming raw messages into model blocks
152 private final EnergyBlockParser energyBlockParser = new EnergyBlockParser();
154 * This is the task used to poll the device
156 private volatile @Nullable AbstractBasePoller systemInformationPoller = null;
158 * This is the task used to poll the device
160 private volatile @Nullable AbstractBasePoller energyPoller = null;
162 * This is the task used to poll the device
164 private volatile @Nullable AbstractBasePoller systemStatePoller = null;
166 * This is the task used to poll the device
168 private volatile @Nullable AbstractBasePoller systemParameterPoller = null;
170 * Communication interface to the slave endpoint we're connecting to
172 protected volatile @Nullable ModbusCommunicationInterface comms = null;
175 * This is the slave id, we store this once initialization is complete
177 private volatile int slaveId;
180 * Instances of this handler should get a reference to the modbus manager
182 * @param thing the thing to handle
183 * @param modbusManager the modbus manager
185 public StiebelEltronHandler(Thing thing) {
190 * @param address address of the value to be written on the modbus
191 * @param shortValue value to be written on the modbus
193 protected void writeInt16(int address, short shortValue) {
194 StiebelEltronConfiguration myconfig = StiebelEltronHandler.this.config;
195 ModbusCommunicationInterface mycomms = StiebelEltronHandler.this.comms;
197 if (myconfig == null || mycomms == null) {
198 throw new IllegalStateException("registerPollTask called without proper configuration");
200 // big endian byte ordering
201 byte b1 = (byte) (shortValue >> 8);
202 byte b2 = (byte) shortValue;
204 ModbusRegister register = new ModbusRegister(b1, b2);
205 ModbusRegisterArray data = new ModbusRegisterArray(new ModbusRegister[] { register });
207 ModbusWriteRegisterRequestBlueprint request = new ModbusWriteRegisterRequestBlueprint(slaveId, address, data,
208 false, myconfig.getMaxTries());
210 mycomms.submitOneTimeWrite(request, result -> {
211 if (hasConfigurationError()) {
214 logger.debug("Successful write, matching request {}", request);
215 StiebelEltronHandler.this.updateStatus(ThingStatus.ONLINE);
217 StiebelEltronHandler.this.handleWriteError(failure);
222 * @param command get the value of this command.
223 * @return short the value of the command multiplied by 10 (see datatype 2 in
224 * the stiebel eltron modbus documentation)
226 private short getScaledInt16Value(Command command) throws StiebelEltronException {
227 if (command instanceof QuantityType) {
228 QuantityType<?> c = ((QuantityType<?>) command).toUnit(CELSIUS);
230 return (short) (c.doubleValue() * 10);
232 throw new StiebelEltronException("Unsupported unit");
235 if (command instanceof DecimalType) {
236 DecimalType c = (DecimalType) command;
237 return (short) (c.doubleValue() * 10);
239 throw new StiebelEltronException("Unsupported command type");
243 * @param command get the value of this command.
244 * @return short the value of the command as short
246 private short getInt16Value(Command command) throws StiebelEltronException {
247 if (command instanceof DecimalType) {
248 DecimalType c = (DecimalType) command;
249 return c.shortValue();
251 throw new StiebelEltronException("Unsupported command type");
255 * Handle incoming commands.
258 public void handleCommand(ChannelUID channelUID, Command command) {
259 if (RefreshType.REFRESH == command) {
260 String groupId = channelUID.getGroupId();
261 if (groupId != null) {
262 AbstractBasePoller poller;
264 case GROUP_SYSTEM_STATE:
265 poller = systemStatePoller;
267 case GROUP_SYSTEM_PARAMETER:
268 poller = systemParameterPoller;
270 case GROUP_SYSTEM_INFO:
271 poller = systemInformationPoller;
273 case GROUP_ENERGY_INFO:
274 poller = energyPoller;
280 if (poller != null) {
286 if (GROUP_SYSTEM_PARAMETER.equals(channelUID.getGroupId())) {
287 switch (channelUID.getIdWithoutGroup()) {
288 case CHANNEL_OPERATION_MODE:
289 writeInt16(1500, getInt16Value(command));
291 case CHANNEL_COMFORT_TEMPERATURE_HEATING:
292 writeInt16(1501, getScaledInt16Value(command));
294 case CHANNEL_ECO_TEMPERATURE_HEATING:
295 writeInt16(1502, getScaledInt16Value(command));
297 case CHANNEL_COMFORT_TEMPERATURE_WATER:
298 writeInt16(1509, getScaledInt16Value(command));
300 case CHANNEL_ECO_TEMPERATURE_WATER:
301 writeInt16(1510, getScaledInt16Value(command));
305 } catch (StiebelEltronException error) {
306 if (hasConfigurationError() || getThing().getStatus() == ThingStatus.OFFLINE) {
309 String cls = error.getClass().getName();
310 String msg = error.getMessage();
311 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
312 String.format("Error with: %s: %s", cls, msg));
318 * Initialization: Load the config object of the block Connect to the slave
319 * bridge Start the periodic polling
322 public void initialize() {
323 config = getConfigAs(StiebelEltronConfiguration.class);
324 logger.debug("Initializing thing with properties: {}", thing.getProperties());
330 * This method starts the operation of this handler Connect to the slave bridge
331 * Start the periodic polling1
333 private void startUp() {
339 ModbusEndpointThingHandler slaveEndpointThingHandler = getEndpointThingHandler();
340 if (slaveEndpointThingHandler == null) {
341 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, "Bridge is offline");
346 slaveId = slaveEndpointThingHandler.getSlaveId();
348 comms = slaveEndpointThingHandler.getCommunicationInterface();
349 } catch (EndpointNotInitializedException e) {
350 // this will be handled below as endpoint remains null
354 @SuppressWarnings("null")
355 String label = Optional.ofNullable(getBridge()).map(b -> b.getLabel()).orElse("<null>");
356 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE,
357 String.format("Bridge '%s' not completely initialized", label));
361 if (config == null) {
362 logger.debug("Invalid comms/config/manager ref for stiebel eltron handler");
366 if (systemInformationPoller == null) {
367 AbstractBasePoller poller = new AbstractBasePoller() {
369 protected void handlePolledData(ModbusRegisterArray registers) {
370 handlePolledSystemInformationData(registers);
373 poller.registerPollTask(500, 36, ModbusReadFunctionCode.READ_INPUT_REGISTERS);
374 systemInformationPoller = poller;
376 if (energyPoller == null) {
377 AbstractBasePoller poller = new AbstractBasePoller() {
379 protected void handlePolledData(ModbusRegisterArray registers) {
380 handlePolledEnergyData(registers);
383 poller.registerPollTask(3500, 16, ModbusReadFunctionCode.READ_INPUT_REGISTERS);
384 energyPoller = poller;
386 if (systemStatePoller == null) {
387 AbstractBasePoller poller = new AbstractBasePoller() {
389 protected void handlePolledData(ModbusRegisterArray registers) {
390 handlePolledSystemStateData(registers);
393 poller.registerPollTask(2500, 2, ModbusReadFunctionCode.READ_INPUT_REGISTERS);
394 systemStatePoller = poller;
396 if (systemParameterPoller == null) {
397 AbstractBasePoller poller = new AbstractBasePoller() {
399 protected void handlePolledData(ModbusRegisterArray registers) {
400 handlePolledSystemParameterData(registers);
403 poller.registerPollTask(1500, 11, ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS);
404 systemParameterPoller = poller;
406 updateStatus(ThingStatus.UNKNOWN);
410 * Dispose the binding correctly
413 public void dispose() {
418 * Unregister the poll tasks and release the endpoint reference
420 private void tearDown() {
421 AbstractBasePoller poller = systemInformationPoller;
422 if (poller != null) {
423 logger.debug("Unregistering systemInformationPoller from ModbusManager");
424 poller.unregisterPollTask();
426 systemInformationPoller = null;
429 poller = energyPoller;
430 if (poller != null) {
431 logger.debug("Unregistering energyPoller from ModbusManager");
432 poller.unregisterPollTask();
437 poller = systemStatePoller;
438 if (poller != null) {
439 logger.debug("Unregistering systemStatePoller from ModbusManager");
440 poller.unregisterPollTask();
442 systemStatePoller = null;
445 poller = systemParameterPoller;
446 if (poller != null) {
447 logger.debug("Unregistering systemParameterPoller from ModbusManager");
448 poller.unregisterPollTask();
450 systemParameterPoller = null;
457 * Returns the current slave id from the bridge
459 public int getSlaveId() {
464 * Get the endpoint handler from the bridge this handler is connected to Checks
465 * that we're connected to the right type of bridge
467 * @return the endpoint handler or null if the bridge does not exist
469 private @Nullable ModbusEndpointThingHandler getEndpointThingHandler() {
470 Bridge bridge = getBridge();
471 if (bridge == null) {
472 logger.debug("Bridge is null");
475 if (bridge.getStatus() != ThingStatus.ONLINE) {
476 logger.debug("Bridge is not online");
480 ThingHandler handler = bridge.getHandler();
481 if (handler == null) {
482 logger.debug("Bridge handler is null");
486 if (handler instanceof ModbusEndpointThingHandler) {
487 ModbusEndpointThingHandler slaveEndpoint = (ModbusEndpointThingHandler) handler;
488 return slaveEndpoint;
490 throw new IllegalStateException("Unexpected bridge handler: " + handler.toString());
495 * Returns value divided by the 10
497 * @param value the value to alter
498 * @return the scaled value as a DecimalType
500 protected State getScaled(Number value, Unit<?> unit) {
501 return QuantityType.valueOf(value.doubleValue() / 10, unit);
505 * Returns high value * 1000 + low value
507 * @param high the high value
508 * @param low the low valze
509 * @return the scaled value as a DecimalType
511 protected State getEnergyQuantity(int high, int low) {
512 double value = high * 1000 + low;
513 return QuantityType.valueOf(value, KILOWATT_HOUR);
517 * This method is called each time new data has been polled from the modbus
518 * slave The register array is first parsed, then each of the channels are
519 * updated to the new values
521 * @param registers byte array read from the modbus slave
523 protected void handlePolledSystemInformationData(ModbusRegisterArray registers) {
524 logger.trace("System Information block received, size: {}", registers.size());
526 SystemInformationBlock block = systemInformationBlockParser.parse(registers);
528 // System information group
529 updateState(channelUID(GROUP_SYSTEM_INFO, CHANNEL_FEK_TEMPERATURE), getScaled(block.temperatureFek, CELSIUS));
530 updateState(channelUID(GROUP_SYSTEM_INFO, CHANNEL_FEK_TEMPERATURE_SETPOINT),
531 getScaled(block.temperatureFekSetPoint, CELSIUS));
532 updateState(channelUID(GROUP_SYSTEM_INFO, CHANNEL_FEK_HUMIDITY), getScaled(block.humidityFek, PERCENT));
533 updateState(channelUID(GROUP_SYSTEM_INFO, CHANNEL_FEK_DEWPOINT), getScaled(block.dewpointFek, CELSIUS));
534 updateState(channelUID(GROUP_SYSTEM_INFO, CHANNEL_OUTDOOR_TEMPERATURE),
535 getScaled(block.temperatureOutdoor, CELSIUS));
536 updateState(channelUID(GROUP_SYSTEM_INFO, CHANNEL_HK1_TEMPERATURE), getScaled(block.temperatureHk1, CELSIUS));
537 updateState(channelUID(GROUP_SYSTEM_INFO, CHANNEL_HK1_TEMPERATURE_SETPOINT),
538 getScaled(block.temperatureHk1SetPoint, CELSIUS));
539 updateState(channelUID(GROUP_SYSTEM_INFO, CHANNEL_SUPPLY_TEMPERATURE),
540 getScaled(block.temperatureSupply, CELSIUS));
541 updateState(channelUID(GROUP_SYSTEM_INFO, CHANNEL_RETURN_TEMPERATURE),
542 getScaled(block.temperatureReturn, CELSIUS));
543 updateState(channelUID(GROUP_SYSTEM_INFO, CHANNEL_SOURCE_TEMPERATURE),
544 getScaled(block.temperatureSource, CELSIUS));
545 updateState(channelUID(GROUP_SYSTEM_INFO, CHANNEL_WATER_TEMPERATURE),
546 getScaled(block.temperatureWater, CELSIUS));
547 updateState(channelUID(GROUP_SYSTEM_INFO, CHANNEL_WATER_TEMPERATURE_SETPOINT),
548 getScaled(block.temperatureWaterSetPoint, CELSIUS));
550 resetCommunicationError();
554 * This method is called each time new data has been polled from the modbus
555 * slave The register array is first parsed, then each of the channels are
556 * updated to the new values
558 * @param registers byte array read from the modbus slave
560 protected void handlePolledEnergyData(ModbusRegisterArray registers) {
561 logger.trace("Energy block received, size: {}", registers.size());
563 EnergyBlock block = energyBlockParser.parse(registers);
565 // Energy information group
566 updateState(channelUID(GROUP_ENERGY_INFO, CHANNEL_PRODUCTION_HEAT_TODAY),
567 new QuantityType<>(block.productionHeatToday, KILOWATT_HOUR));
568 updateState(channelUID(GROUP_ENERGY_INFO, CHANNEL_PRODUCTION_HEAT_TOTAL),
569 getEnergyQuantity(block.productionHeatTotalHigh, block.productionHeatTotalLow));
570 updateState(channelUID(GROUP_ENERGY_INFO, CHANNEL_PRODUCTION_WATER_TODAY),
571 new QuantityType<>(block.productionWaterToday, KILOWATT_HOUR));
572 updateState(channelUID(GROUP_ENERGY_INFO, CHANNEL_PRODUCTION_WATER_TOTAL),
573 getEnergyQuantity(block.productionWaterTotalHigh, block.productionWaterTotalLow));
574 updateState(channelUID(GROUP_ENERGY_INFO, CHANNEL_CONSUMPTION_HEAT_TODAY),
575 new QuantityType<>(block.consumptionHeatToday, KILOWATT_HOUR));
576 updateState(channelUID(GROUP_ENERGY_INFO, CHANNEL_CONSUMPTION_HEAT_TOTAL),
577 getEnergyQuantity(block.consumptionHeatTotalHigh, block.consumptionHeatTotalLow));
578 updateState(channelUID(GROUP_ENERGY_INFO, CHANNEL_CONSUMPTION_WATER_TODAY),
579 new QuantityType<>(block.consumptionWaterToday, KILOWATT_HOUR));
580 updateState(channelUID(GROUP_ENERGY_INFO, CHANNEL_CONSUMPTION_WATER_TOTAL),
581 getEnergyQuantity(block.consumptionWaterTotalHigh, block.consumptionWaterTotalLow));
583 resetCommunicationError();
587 * This method is called each time new data has been polled from the modbus
588 * slave The register array is first parsed, then each of the channels are
589 * updated to the new values
591 * @param registers byte array read from the modbus slave
593 protected void handlePolledSystemStateData(ModbusRegisterArray registers) {
594 logger.trace("System state block received, size: {}", registers.size());
596 SystemStateBlock block = systemstateBlockParser.parse(registers);
597 boolean isHeating = (block.state & 16) != 0;
598 updateState(channelUID(GROUP_SYSTEM_STATE, CHANNEL_IS_HEATING),
599 isHeating ? OpenClosedType.OPEN : OpenClosedType.CLOSED);
600 updateState(channelUID(GROUP_SYSTEM_STATE, CHANNEL_IS_HEATING_WATER),
601 (block.state & 32) != 0 ? OpenClosedType.OPEN : OpenClosedType.CLOSED);
602 updateState(channelUID(GROUP_SYSTEM_STATE, CHANNEL_IS_COOLING),
603 (block.state & 256) != 0 ? OpenClosedType.OPEN : OpenClosedType.CLOSED);
604 updateState(channelUID(GROUP_SYSTEM_STATE, CHANNEL_IS_SUMMER),
605 (block.state & 128) != 0 ? OpenClosedType.OPEN : OpenClosedType.CLOSED);
606 updateState(channelUID(GROUP_SYSTEM_STATE, CHANNEL_IS_PUMPING),
607 (block.state & 1) != 0 ? OpenClosedType.OPEN : OpenClosedType.CLOSED);
609 resetCommunicationError();
613 * This method is called each time new data has been polled from the modbus
614 * slave The register array is first parsed, then each of the channels are
615 * updated to the new values
617 * @param registers byte array read from the modbus slave
619 protected void handlePolledSystemParameterData(ModbusRegisterArray registers) {
620 logger.trace("System state block received, size: {}", registers.size());
622 SystemParameterBlock block = systemParameterBlockParser.parse(registers);
623 updateState(channelUID(GROUP_SYSTEM_PARAMETER, CHANNEL_OPERATION_MODE), new DecimalType(block.operationMode));
625 updateState(channelUID(GROUP_SYSTEM_PARAMETER, CHANNEL_COMFORT_TEMPERATURE_HEATING),
626 getScaled(block.comfortTemperatureHeating, CELSIUS));
627 updateState(channelUID(GROUP_SYSTEM_PARAMETER, CHANNEL_ECO_TEMPERATURE_HEATING),
628 getScaled(block.ecoTemperatureHeating, CELSIUS));
629 updateState(channelUID(GROUP_SYSTEM_PARAMETER, CHANNEL_COMFORT_TEMPERATURE_WATER),
630 getScaled(block.comfortTemperatureWater, CELSIUS));
631 updateState(channelUID(GROUP_SYSTEM_PARAMETER, CHANNEL_ECO_TEMPERATURE_WATER),
632 getScaled(block.ecoTemperatureWater, CELSIUS));
634 resetCommunicationError();
638 * @param bridgeStatusInfo
641 public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
642 super.bridgeStatusChanged(bridgeStatusInfo);
644 if (bridgeStatusInfo.getStatus() == ThingStatus.ONLINE) {
646 } else if (bridgeStatusInfo.getStatus() == ThingStatus.OFFLINE) {
652 * Handle errors received during communication
654 protected void handleReadError(AsyncModbusFailure<ModbusReadRequestBlueprint> failure) {
655 // Ignore all incoming data and errors if configuration is not correct
656 if (hasConfigurationError() || getThing().getStatus() == ThingStatus.OFFLINE) {
659 String msg = failure.getCause().getMessage();
660 String cls = failure.getCause().getClass().getName();
661 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
662 String.format("Error with read: %s: %s", cls, msg));
666 * Handle errors received during communication
668 protected void handleWriteError(AsyncModbusFailure<ModbusWriteRequestBlueprint> failure) {
669 // Ignore all incoming data and errors if configuration is not correct
670 if (hasConfigurationError() || getThing().getStatus() == ThingStatus.OFFLINE) {
673 String msg = failure.getCause().getMessage();
674 String cls = failure.getCause().getClass().getName();
675 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
676 String.format("Error with write: %s: %s", cls, msg));
680 * Returns true, if we're in a CONFIGURATION_ERROR state
684 protected boolean hasConfigurationError() {
685 ThingStatusInfo statusInfo = getThing().getStatusInfo();
686 return statusInfo.getStatus() == ThingStatus.OFFLINE
687 && statusInfo.getStatusDetail() == ThingStatusDetail.CONFIGURATION_ERROR;
691 * Reset communication status to ONLINE if we're in an OFFLINE state
693 protected void resetCommunicationError() {
694 ThingStatusInfo statusInfo = thing.getStatusInfo();
695 if (ThingStatus.OFFLINE.equals(statusInfo.getStatus())
696 && ThingStatusDetail.COMMUNICATION_ERROR.equals(statusInfo.getStatusDetail())) {
697 updateStatus(ThingStatus.ONLINE);
702 * Returns the channel UID for the specified group and channel id
704 * @param string the channel group
705 * @param string the channel id in that group
706 * @return the globally unique channel uid
708 ChannelUID channelUID(String group, String id) {
709 return new ChannelUID(getThing().getUID(), group, id);