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.teleinfo.internal.serial;
15 import static org.openhab.binding.teleinfo.internal.TeleinfoBindingConstants.*;
17 import java.io.IOException;
18 import java.util.concurrent.ScheduledFuture;
19 import java.util.concurrent.TimeUnit;
21 import org.eclipse.jdt.annotation.NonNullByDefault;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.openhab.binding.teleinfo.internal.data.Frame;
24 import org.openhab.binding.teleinfo.internal.handler.TeleinfoAbstractControllerHandler;
25 import org.openhab.binding.teleinfo.internal.reader.io.serialport.InvalidFrameException;
26 import org.openhab.core.io.transport.serial.PortInUseException;
27 import org.openhab.core.io.transport.serial.SerialPort;
28 import org.openhab.core.io.transport.serial.SerialPortIdentifier;
29 import org.openhab.core.io.transport.serial.SerialPortManager;
30 import org.openhab.core.io.transport.serial.UnsupportedCommOperationException;
31 import org.openhab.core.library.types.DecimalType;
32 import org.openhab.core.thing.Bridge;
33 import org.openhab.core.thing.ChannelUID;
34 import org.openhab.core.thing.ThingStatus;
35 import org.openhab.core.thing.ThingStatusDetail;
36 import org.openhab.core.types.Command;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
41 * The {@link TeleinfoSerialControllerHandler} class defines a handler for serial controller.
43 * @author Nicolas SIBERIL - Initial contribution
46 public class TeleinfoSerialControllerHandler extends TeleinfoAbstractControllerHandler
47 implements TeleinfoReceiveThreadListener {
49 private final Logger logger = LoggerFactory.getLogger(TeleinfoSerialControllerHandler.class);
51 private static final int SERIAL_RECEIVE_TIMEOUT_MS = 250;
52 private static final int RECEIVER_THREAD_JOIN_DELAY_MS = 500;
54 private SerialPortManager serialPortManager;
55 private @Nullable SerialPort serialPort;
56 private @Nullable TeleinfoReceiveThread receiveThread;
57 private @Nullable ScheduledFuture<?> keepAliveThread;
58 private long invalidFrameCounter = 0;
60 public TeleinfoSerialControllerHandler(Bridge thing, SerialPortManager serialPortManager) {
62 this.serialPortManager = serialPortManager;
66 public void initialize() {
67 invalidFrameCounter = 0;
69 keepAliveThread = scheduler.scheduleWithFixedDelay(() -> {
70 if (!isInitialized()) {
71 openSerialPortAndStartReceiving();
72 updateStatus(ThingStatus.UNKNOWN);
74 logger.debug("Check Teleinfo receiveThread status...");
75 logger.debug("isInitialized() = {}", isInitialized());
76 TeleinfoReceiveThread receiveThreadRef = receiveThread;
77 if (receiveThreadRef != null) {
78 logger.debug("receiveThread.isAlive() = {}", receiveThreadRef.isAlive());
80 if (isInitialized() && (receiveThreadRef == null || !receiveThreadRef.isAlive())) {
81 updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, ERROR_UNKNOWN_RETRY_IN_PROGRESS);
82 logger.info("Try to restart Teleinfo receiving...");
83 stopReceivingAndCloseSerialPort();
84 openSerialPortAndStartReceiving();
86 }, 0, 60, TimeUnit.SECONDS);
90 public void dispose() {
91 ScheduledFuture<?> keepAliveThreadRef = keepAliveThread;
92 if (keepAliveThreadRef != null) {
93 keepAliveThreadRef.cancel(true);
94 keepAliveThread = null;
96 stopReceivingAndCloseSerialPort();
102 public void handleCommand(ChannelUID channelUID, Command command) {
106 public void onFrameReceived(Frame frame) {
107 updateStatus(ThingStatus.ONLINE);
108 fireOnFrameReceivedEvent(frame);
112 public void onInvalidFrameReceived(TeleinfoReceiveThread receiveThread, InvalidFrameException error) {
113 invalidFrameCounter++;
114 updateState(THING_SERIAL_CONTROLLER_CHANNEL_INVALID_FRAME_COUNTER, new DecimalType(invalidFrameCounter));
118 public void onSerialPortInputStreamIOException(TeleinfoReceiveThread receiveThread, IOException e) {
119 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, ERROR_UNKNOWN_RETRY_IN_PROGRESS);
123 public void continueOnReadNextFrameTimeoutException() {
124 updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, ERROR_UNKNOWN_RETRY_IN_PROGRESS);
127 private void openSerialPortAndStartReceiving() {
128 TeleinfoSerialControllerConfiguration config = getConfigAs(TeleinfoSerialControllerConfiguration.class);
130 if (config.serialport.trim().isEmpty()) {
131 logger.warn("Teleinfo port is not set.");
135 logger.debug("Connecting to serial port '{}'...", config.serialport);
136 String currentOwner = null;
138 final SerialPortIdentifier portIdentifier = serialPortManager.getIdentifier(config.serialport);
139 logger.debug("portIdentifier = {}", portIdentifier);
140 if (portIdentifier == null) {
141 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
142 ERROR_OFFLINE_SERIAL_NOT_FOUND);
145 logger.debug("Opening portIdentifier");
146 currentOwner = portIdentifier.getCurrentOwner();
147 logger.debug("portIdentifier.getCurrentOwner() = {}", currentOwner);
148 SerialPort commPort = portIdentifier.open("org.openhab.binding.teleinfo", 5000);
149 serialPort = commPort;
151 TeleinfoTicMode ticMode = TeleinfoTicMode.valueOf(config.ticMode);
152 commPort.setSerialPortParams(ticMode.getBitrate(), SerialPort.DATABITS_7, SerialPort.STOPBITS_1,
153 SerialPort.PARITY_EVEN);
155 commPort.enableReceiveThreshold(1);
156 } catch (UnsupportedCommOperationException e) {
160 commPort.enableReceiveTimeout(SERIAL_RECEIVE_TIMEOUT_MS);
161 } catch (UnsupportedCommOperationException e) {
164 logger.debug("Starting receive thread");
165 TeleinfoReceiveThread receiveThread = new TeleinfoReceiveThread(commPort, this,
166 config.autoRepairInvalidADPSgroupLine, ticMode, config.verifyChecksum);
167 this.receiveThread = receiveThread;
168 receiveThread.start();
170 logger.debug("Connected to serial port '{}'", config.serialport);
171 } catch (PortInUseException e) {
172 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
173 ERROR_OFFLINE_SERIAL_INUSE);
174 } catch (UnsupportedCommOperationException e) {
175 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
176 ERROR_OFFLINE_SERIAL_UNSUPPORTED);
180 private void stopReceivingAndCloseSerialPort() {
181 TeleinfoReceiveThread receiveThreadRef = receiveThread;
182 if (receiveThreadRef != null) {
183 receiveThreadRef.interrupt();
185 receiveThreadRef.join(RECEIVER_THREAD_JOIN_DELAY_MS);
186 } catch (InterruptedException e) {
188 receiveThreadRef.setListener(null);
189 receiveThread = null;
191 SerialPort serialPortRef = serialPort;
192 if (serialPortRef != null) {
193 serialPortRef.close();