]> git.basschouten.com Git - openhab-addons.git/blob
b665e675a953d16fecb54bcd192f47af4539e87a
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.teleinfo.internal.handler;
14
15 import static org.openhab.binding.teleinfo.internal.TeleinfoBindingConstants.*;
16
17 import java.util.Map.Entry;
18
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;
45
46 /**
47  * The {@link TeleinfoElectricityMeterHandler} class defines a skeleton for Electricity Meters handlers.
48  *
49  * @author Nicolas SIBERIL - Initial contribution
50  */
51 @NonNullByDefault
52 public class TeleinfoElectricityMeterHandler extends BaseThingHandler implements TeleinfoControllerHandlerListener {
53
54     private final Logger logger = LoggerFactory.getLogger(TeleinfoElectricityMeterHandler.class);
55     protected TeleinfoElectricityMeterConfiguration configuration = new TeleinfoElectricityMeterConfiguration();
56     private boolean wasLastFrameShort = false;
57
58     public TeleinfoElectricityMeterHandler(Thing thing) {
59         super(thing);
60     }
61
62     @Override
63     public void initialize() {
64         updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, ERROR_OFFLINE_CONTROLLER_OFFLINE);
65
66         Bridge bridge = getBridge();
67         logger.debug("bridge = {}", bridge);
68         if (bridge != null) {
69             bridgeStatusChanged(bridge.getStatusInfo());
70         }
71         configuration = getConfigAs(TeleinfoElectricityMeterConfiguration.class);
72     }
73
74     @Override
75     public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
76         TeleinfoAbstractControllerHandler controllerHandler = getControllerHandler();
77         if (bridgeStatusInfo.getStatus() != ThingStatus.ONLINE) {
78             if (controllerHandler != null) {
79                 controllerHandler.removeListener(this);
80             }
81             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, ERROR_OFFLINE_CONTROLLER_OFFLINE);
82             return;
83         }
84
85         if (controllerHandler != null) {
86             controllerHandler.addListener(this);
87             updateStatus(ThingStatus.ONLINE);
88         }
89     }
90
91     @Override
92     public void dispose() {
93         TeleinfoAbstractControllerHandler controllerHandler = getControllerHandler();
94         if (controllerHandler != null) {
95             controllerHandler.removeListener(this);
96         }
97         super.dispose();
98     }
99
100     private @Nullable TeleinfoAbstractControllerHandler getControllerHandler() {
101         Bridge bridge = getBridge();
102         return bridge != null ? (TeleinfoAbstractControllerHandler) bridge.getHandler() : null;
103     }
104
105     @Override
106     public void handleCommand(ChannelUID channelUID, Command command) {
107         // no commands supported
108     }
109
110     @Override
111     protected void updateStatus(ThingStatus status, ThingStatusDetail statusDetail, @Nullable String description) {
112         super.updateStatus(status, statusDetail, description);
113
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);
118                 }
119             }
120         }
121     }
122
123     @Override
124     protected void updateStatus(ThingStatus status, ThingStatusDetail statusDetail) {
125         this.updateStatus(status, statusDetail, null);
126     }
127
128     @Override
129     protected void updateStatus(ThingStatus status) {
130         this.updateStatus(status, ThingStatusDetail.NONE, null);
131     }
132
133     @Override
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);
138         }
139     }
140
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()));
153                 }
154             }
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));
160                 }
161             }
162         }
163         try {
164             if (frame.getTicMode() == TeleinfoTicMode.HISTORICAL) {
165                 try {
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()));
171                     }
172                 } catch (InvalidFrameException e) {
173                     logger.warn("Can not find pricing option.");
174                 }
175
176                 try {
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);
185                         }
186                         wasLastFrameShort = frame.isShortFrame();
187                     }
188                 } catch (InvalidFrameException e) {
189                     logger.warn("Can not find phase.");
190                 }
191             } else {
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]));
198                         }
199                     }
200                 }
201             }
202         } catch (InvalidFrameException e) {
203             logger.warn("Can not find TIC mode.");
204         }
205
206         updateState(CHANNEL_LAST_UPDATE, new DateTimeType());
207     }
208
209     private void updateStateForMissingAlert(Frame frame, Label label) {
210         if (!frame.getLabelToValues().containsKey(label)) {
211             updateState(label.getChannelName(), UnDefType.NULL);
212         }
213     }
214 }