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.handler;
15 import static org.openhab.binding.teleinfo.internal.TeleinfoBindingConstants.*;
17 import java.util.Map.Entry;
19 import org.eclipse.jdt.annotation.NonNullByDefault;
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.openhab.binding.teleinfo.internal.data.Frame;
22 import org.openhab.binding.teleinfo.internal.data.Phase;
23 import org.openhab.binding.teleinfo.internal.data.Pricing;
24 import org.openhab.binding.teleinfo.internal.reader.io.serialport.FrameUtil;
25 import org.openhab.binding.teleinfo.internal.reader.io.serialport.InvalidFrameException;
26 import org.openhab.binding.teleinfo.internal.reader.io.serialport.Label;
27 import org.openhab.binding.teleinfo.internal.reader.io.serialport.ValueType;
28 import org.openhab.binding.teleinfo.internal.serial.TeleinfoTicMode;
29 import org.openhab.core.library.types.DateTimeType;
30 import org.openhab.core.library.types.OnOffType;
31 import org.openhab.core.library.types.QuantityType;
32 import org.openhab.core.library.types.StringType;
33 import org.openhab.core.thing.Bridge;
34 import org.openhab.core.thing.Channel;
35 import org.openhab.core.thing.ChannelUID;
36 import org.openhab.core.thing.Thing;
37 import org.openhab.core.thing.ThingStatus;
38 import org.openhab.core.thing.ThingStatusDetail;
39 import org.openhab.core.thing.ThingStatusInfo;
40 import org.openhab.core.thing.binding.BaseThingHandler;
41 import org.openhab.core.types.Command;
42 import org.openhab.core.types.UnDefType;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
47 * The {@link TeleinfoElectricityMeterHandler} class defines a skeleton for Electricity Meters handlers.
49 * @author Nicolas SIBERIL - Initial contribution
52 public class TeleinfoElectricityMeterHandler extends BaseThingHandler implements TeleinfoControllerHandlerListener {
54 private final Logger logger = LoggerFactory.getLogger(TeleinfoElectricityMeterHandler.class);
55 protected TeleinfoElectricityMeterConfiguration configuration = new TeleinfoElectricityMeterConfiguration();
56 private boolean wasLastFrameShort = false;
58 public TeleinfoElectricityMeterHandler(Thing thing) {
63 public void initialize() {
64 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, ERROR_OFFLINE_CONTROLLER_OFFLINE);
66 Bridge bridge = getBridge();
67 logger.debug("bridge = {}", bridge);
69 bridgeStatusChanged(bridge.getStatusInfo());
71 configuration = getConfigAs(TeleinfoElectricityMeterConfiguration.class);
75 public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
76 TeleinfoAbstractControllerHandler controllerHandler = getControllerHandler();
77 if (bridgeStatusInfo.getStatus() != ThingStatus.ONLINE) {
78 if (controllerHandler != null) {
79 controllerHandler.removeListener(this);
81 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, ERROR_OFFLINE_CONTROLLER_OFFLINE);
85 if (controllerHandler != null) {
86 controllerHandler.addListener(this);
87 updateStatus(ThingStatus.ONLINE);
92 public void dispose() {
93 TeleinfoAbstractControllerHandler controllerHandler = getControllerHandler();
94 if (controllerHandler != null) {
95 controllerHandler.removeListener(this);
100 private @Nullable TeleinfoAbstractControllerHandler getControllerHandler() {
101 Bridge bridge = getBridge();
102 return bridge != null ? (TeleinfoAbstractControllerHandler) bridge.getHandler() : null;
106 public void handleCommand(ChannelUID channelUID, Command command) {
107 // no commands supported
111 protected void updateStatus(ThingStatus status, ThingStatusDetail statusDetail, @Nullable String description) {
112 super.updateStatus(status, statusDetail, description);
114 if (!(ThingStatus.ONLINE.equals(status))) {
115 for (Channel channel : getThing().getChannels()) {
116 if (!CHANNEL_LAST_UPDATE.equals(channel.getUID().getId())) {
117 updateState(channel.getUID(), UnDefType.UNDEF);
124 protected void updateStatus(ThingStatus status, ThingStatusDetail statusDetail) {
125 this.updateStatus(status, statusDetail, null);
129 protected void updateStatus(ThingStatus status) {
130 this.updateStatus(status, ThingStatusDetail.NONE, null);
134 public void onFrameReceived(Frame frame) {
135 String adco = configuration.getAdco();
136 if (adco.equalsIgnoreCase(frame.get(Label.ADCO)) || adco.equalsIgnoreCase(frame.get(Label.ADSC))) {
137 updateStatesForChannels(frame);
141 private void updateStatesForChannels(Frame frame) {
142 for (Entry<Label, String> entry : frame.getLabelToValues().entrySet()) {
143 Label label = entry.getKey();
144 if (!label.getChannelName().equals(NOT_A_CHANNEL)) {
145 logger.trace("Update channel {} to value {}", label.getChannelName(), entry.getValue());
146 if (label == Label.PTEC) {
147 updateState(label.getChannelName(), StringType.valueOf(entry.getValue().replace(".", "")));
148 } else if (label.getType() == ValueType.STRING) {
149 updateState(label.getChannelName(), StringType.valueOf(entry.getValue()));
150 } else if (label.getType() == ValueType.INTEGER) {
151 updateState(label.getChannelName(), QuantityType
152 .valueOf(label.getFactor() * Integer.parseInt(entry.getValue()), label.getUnit()));
155 if (!label.getTimestampChannelName().equals(NOT_A_CHANNEL)) {
156 String timestamp = frame.getAsDateTime(label);
157 if (!timestamp.isEmpty()) {
158 logger.trace("Update channel {} to value {}", label.getTimestampChannelName(), timestamp);
159 updateState(label.getTimestampChannelName(), DateTimeType.valueOf(timestamp));
164 if (frame.getTicMode() == TeleinfoTicMode.HISTORICAL) {
166 if (frame.getPricing() == Pricing.TEMPO) {
167 updateState(CHANNEL_TEMPO_FRAME_PROGRAMME_CIRCUIT_1,
168 StringType.valueOf(frame.getProgrammeCircuit1()));
169 updateState(CHANNEL_TEMPO_FRAME_PROGRAMME_CIRCUIT_2,
170 StringType.valueOf(frame.getProgrammeCircuit2()));
172 } catch (InvalidFrameException e) {
173 logger.warn("Can not find pricing option.");
177 Phase phase = frame.getPhase();
178 if (phase == Phase.ONE_PHASED) {
179 updateStateForMissingAlert(frame, Label.ADPS);
180 } else if (phase == Phase.THREE_PHASED) {
181 if (!wasLastFrameShort) {
182 updateStateForMissingAlert(frame, Label.ADIR1);
183 updateStateForMissingAlert(frame, Label.ADIR2);
184 updateStateForMissingAlert(frame, Label.ADIR3);
186 wasLastFrameShort = frame.isShortFrame();
188 } catch (InvalidFrameException e) {
189 logger.warn("Can not find phase.");
192 if (frame.getLabelToValues().containsKey(Label.RELAIS)) {
193 String relaisString = frame.get(Label.RELAIS);
194 if (relaisString != null) {
195 boolean[] relaisStates = FrameUtil.parseRelaisStates(relaisString);
196 for (int i = 0; i <= 7; i++) {
197 updateState(CHANNELS_LSM_RELAIS[i], OnOffType.from(relaisStates[i]));
202 } catch (InvalidFrameException e) {
203 logger.warn("Can not find TIC mode.");
206 updateState(CHANNEL_LAST_UPDATE, new DateTimeType());
209 private void updateStateForMissingAlert(Frame frame, Label label) {
210 if (!frame.getLabelToValues().containsKey(label)) {
211 updateState(label.getChannelName(), UnDefType.NULL);