]> git.basschouten.com Git - openhab-addons.git/blob
710aeccb8660b731368da0245097a18f16287192
[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.modbus.sunspec.internal.handler;
14
15 import static org.openhab.binding.modbus.sunspec.internal.SunSpecConstants.*;
16 import static org.openhab.core.library.unit.Units.*;
17
18 import org.eclipse.jdt.annotation.NonNullByDefault;
19 import org.openhab.binding.modbus.sunspec.internal.dto.MeterModelBlock;
20 import org.openhab.binding.modbus.sunspec.internal.parser.MeterModelParser;
21 import org.openhab.core.io.transport.modbus.ModbusRegisterArray;
22 import org.openhab.core.thing.Thing;
23 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
25
26 /**
27  * This handler is responsible for handling data recieved from a sunspec meter
28  *
29  * @author Nagy Attila Gabor - Initial contribution
30  *
31  */
32 @NonNullByDefault
33 public class MeterHandler extends AbstractSunSpecHandler {
34
35     /**
36      * Parser used to convert incoming raw messages into model blocks
37      */
38     private final MeterModelParser parser = new MeterModelParser();
39
40     /**
41      * Logger instance
42      */
43     private final Logger logger = LoggerFactory.getLogger(MeterHandler.class);
44
45     public MeterHandler(Thing thing) {
46         super(thing);
47     }
48
49     /**
50      * Receive polled data, parse then update states
51      */
52     @Override
53     protected void handlePolledData(ModbusRegisterArray registers) {
54         logger.trace("Model block received, size: {}", registers.size());
55
56         MeterModelBlock block = parser.parse(registers);
57
58         // AC General group
59         updateTotalValues(block);
60
61         updatePhaseValues(block, block.phaseA, GROUP_AC_PHASE_A);
62
63         // Split phase, wye/delta phase
64         if (block.sunspecDID >= METER_SPLIT_PHASE && (thing.getThingTypeUID().equals(THING_TYPE_METER_SPLIT_PHASE)
65                 || thing.getThingTypeUID().equals(THING_TYPE_METER_WYE_PHASE)
66                 || thing.getThingTypeUID().equals(THING_TYPE_METER_DELTA_PHASE))) {
67             updatePhaseValues(block, block.phaseB, GROUP_AC_PHASE_B);
68         }
69
70         // Three phase (wye/delta) only
71         if (block.sunspecDID >= INVERTER_THREE_PHASE && (thing.getThingTypeUID().equals(THING_TYPE_METER_WYE_PHASE)
72                 || thing.getThingTypeUID().equals(THING_TYPE_METER_DELTA_PHASE))) {
73             updatePhaseValues(block, block.phaseC, GROUP_AC_PHASE_C);
74         }
75
76         resetCommunicationError();
77     }
78
79     /**
80      * Update the total states from the received block
81      *
82      * @param block
83      */
84     private void updateTotalValues(MeterModelBlock block) {
85         updateState(channelUID(GROUP_AC_GENERAL, CHANNEL_AC_TOTAL_CURRENT),
86                 getScaled(block.acCurrentTotal, block.acCurrentSF, AMPERE));
87
88         updateState(channelUID(GROUP_AC_GENERAL, CHANNEL_AC_AVERAGE_VOLTAGE_TO_N),
89                 getScaled(block.acVoltageLineToNAverage, block.acVoltageSF, VOLT));
90
91         updateState(channelUID(GROUP_AC_GENERAL, CHANNEL_AC_AVERAGE_VOLTAGE_TO_NEXT),
92                 getScaled(block.acVoltageLineToLineAverage, block.acVoltageSF, VOLT));
93
94         updateState(channelUID(GROUP_AC_GENERAL, CHANNEL_AC_FREQUENCY),
95                 getScaled(block.acFrequency, block.acFrequencySF.orElse((short) 1), HERTZ));
96
97         updateState(channelUID(GROUP_AC_GENERAL, CHANNEL_AC_TOTAL_REAL_POWER),
98                 getScaled(block.acRealPowerTotal, block.acRealPowerSF, WATT));
99
100         updateState(channelUID(GROUP_AC_GENERAL, CHANNEL_AC_TOTAL_APPARENT_POWER),
101                 getScaled(block.acApparentPowerTotal, block.acApparentPowerSF, WATT)); // TODO: this should be VA
102
103         updateState(channelUID(GROUP_AC_GENERAL, CHANNEL_AC_TOTAL_REACTIVE_POWER),
104                 getScaled(block.acReactivePowerTotal, block.acReactivePowerSF, WATT)); // TODO: this should be VAR
105
106         updateState(channelUID(GROUP_AC_GENERAL, CHANNEL_AC_AVERAGE_POWER_FACTOR),
107                 getScaled(block.acPowerFactor, block.acPowerFactorSF, PERCENT));
108
109         updateState(channelUID(GROUP_AC_GENERAL, CHANNEL_AC_TOTAL_EXPORTED_REAL_ENERGY),
110                 getScaled(block.acExportedRealEnergyTotal, block.acRealEnergySF, WATT_HOUR));
111
112         updateState(channelUID(GROUP_AC_GENERAL, CHANNEL_AC_TOTAL_IMPORTED_REAL_ENERGY),
113                 getScaled(block.acImportedRealEnergyTotal, block.acRealEnergySF, WATT_HOUR));
114
115         updateState(channelUID(GROUP_AC_GENERAL, CHANNEL_AC_TOTAL_EXPORTED_APPARENT_ENERGY),
116                 getScaled(block.acExportedApparentEnergyTotal, block.acApparentEnergySF, WATT_HOUR)); // TODO: this
117                                                                                                       // should be
118                                                                                                       // VA_HOUR
119
120         updateState(channelUID(GROUP_AC_GENERAL, CHANNEL_AC_TOTAL_IMPORTED_APPARENT_ENERGY),
121                 getScaled(block.acImportedApparentEnergyTotal, block.acApparentEnergySF, WATT_HOUR)); // TODO: this
122                                                                                                       // should be
123                                                                                                       // VA_HOUR
124
125         updateState(channelUID(GROUP_AC_GENERAL, CHANNEL_AC_TOTAL_IMPORTED_REACTIVE_ENERGY_Q1),
126                 getScaled(block.acImportedReactiveEnergyQ1Total, block.acReactiveEnergySF, WATT_HOUR)); // TODO: this
127                                                                                                         // should be
128                                                                                                         // VAR_HOUR
129
130         updateState(channelUID(GROUP_AC_GENERAL, CHANNEL_AC_TOTAL_IMPORTED_REACTIVE_ENERGY_Q2),
131                 getScaled(block.acImportedReactiveEnergyQ2Total, block.acReactiveEnergySF, WATT_HOUR)); // TODO: this
132                                                                                                         // should be
133                                                                                                         // VAR_HOUR
134
135         updateState(channelUID(GROUP_AC_GENERAL, CHANNEL_AC_TOTAL_EXPORTED_REACTIVE_ENERGY_Q3),
136                 getScaled(block.acExportedReactiveEnergyQ3Total, block.acReactiveEnergySF, WATT_HOUR)); // TODO: this
137                                                                                                         // should be
138                                                                                                         // VAR_HOUR
139
140         updateState(channelUID(GROUP_AC_GENERAL, CHANNEL_AC_TOTAL_EXPORTED_REACTIVE_ENERGY_Q4),
141                 getScaled(block.acExportedReactiveEnergyQ4Total, block.acReactiveEnergySF, WATT_HOUR)); // TODO: this
142                                                                                                         // should be
143                                                                                                         // VAR_HOUR
144     }
145
146     /**
147      * Update phase related channels for the selected phase.
148      *
149      * @param block the main block for scale
150      * @param phaseBlock the block containing the raw values for the selected phase
151      * @param group channel group id for the output
152      */
153     private void updatePhaseValues(MeterModelBlock block, MeterModelBlock.PhaseBlock phaseBlock, String group) {
154         updateState(channelUID(group, CHANNEL_AC_PHASE_CURRENT),
155                 getScaled(phaseBlock.acPhaseCurrent, block.acCurrentSF, AMPERE));
156
157         updateState(channelUID(group, CHANNEL_AC_VOLTAGE_TO_N),
158                 getScaled(phaseBlock.acVoltageToN, block.acVoltageSF, VOLT));
159
160         updateState(channelUID(group, CHANNEL_AC_VOLTAGE_TO_NEXT),
161                 getScaled(phaseBlock.acVoltageToNext, block.acVoltageSF, VOLT));
162
163         updateState(channelUID(group, CHANNEL_AC_REAL_POWER),
164                 getScaled(phaseBlock.acRealPower, block.acRealPowerSF, WATT));
165
166         updateState(channelUID(group, CHANNEL_AC_APPARENT_POWER),
167                 getScaled(phaseBlock.acApparentPower, block.acApparentPowerSF, WATT)); // TODO: this should be VA
168
169         updateState(channelUID(group, CHANNEL_AC_REACTIVE_POWER),
170                 getScaled(phaseBlock.acReactivePower, block.acReactivePowerSF, WATT)); // TODO: this should be VAR
171
172         updateState(channelUID(group, CHANNEL_AC_POWER_FACTOR),
173                 getScaled(phaseBlock.acPowerFactor, block.acPowerFactorSF, PERCENT));
174
175         updateState(channelUID(group, CHANNEL_AC_EXPORTED_REAL_ENERGY),
176                 getScaled(phaseBlock.acExportedRealEnergy, block.acRealEnergySF, WATT_HOUR));
177
178         updateState(channelUID(group, CHANNEL_AC_IMPORTED_REAL_ENERGY),
179                 getScaled(phaseBlock.acImportedRealEnergy, block.acRealEnergySF, WATT_HOUR));
180
181         updateState(channelUID(group, CHANNEL_AC_EXPORTED_APPARENT_ENERGY),
182                 getScaled(phaseBlock.acExportedApparentEnergy, block.acApparentEnergySF, WATT_HOUR)); // TODO: this
183                                                                                                       // should be
184                                                                                                       // VA_HOUR
185
186         updateState(channelUID(group, CHANNEL_AC_IMPORTED_APPARENT_ENERGY),
187                 getScaled(phaseBlock.acImportedApparentEnergy, block.acApparentEnergySF, WATT_HOUR)); // TODO: this
188                                                                                                       // should be
189                                                                                                       // VA_HOUR
190
191         updateState(channelUID(group, CHANNEL_AC_IMPORTED_REACTIVE_ENERGY_Q1),
192                 getScaled(phaseBlock.acImportedReactiveEnergyQ1, block.acReactiveEnergySF, WATT_HOUR)); // TODO: this
193                                                                                                         // should be
194                                                                                                         // VAR_HOUR
195
196         updateState(channelUID(group, CHANNEL_AC_IMPORTED_REACTIVE_ENERGY_Q2),
197                 getScaled(phaseBlock.acImportedReactiveEnergyQ2, block.acReactiveEnergySF, WATT_HOUR)); // TODO: this
198                                                                                                         // should be
199                                                                                                         // VAR_HOUR
200
201         updateState(channelUID(group, CHANNEL_AC_EXPORTED_REACTIVE_ENERGY_Q3),
202                 getScaled(phaseBlock.acExportedReactiveEnergyQ3, block.acReactiveEnergySF, WATT_HOUR)); // TODO: this
203                                                                                                         // should be
204                                                                                                         // VAR_HOUR
205
206         updateState(channelUID(group, CHANNEL_AC_EXPORTED_REACTIVE_ENERGY_Q4),
207                 getScaled(phaseBlock.acExportedReactiveEnergyQ4, block.acReactiveEnergySF, WATT_HOUR)); // TODO: this
208                                                                                                         // should be
209                                                                                                         // VAR_HOUR
210     }
211 }