]> git.basschouten.com Git - openhab-addons.git/blob
092813c0d355b66fa9f8088e5ceeec8072301caa
[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.sonnen.internal;
14
15 import static org.openhab.binding.sonnen.internal.SonnenBindingConstants.*;
16
17 import java.util.HashMap;
18 import java.util.Map;
19 import java.util.concurrent.ScheduledFuture;
20 import java.util.concurrent.TimeUnit;
21
22 import javax.measure.quantity.Dimensionless;
23 import javax.measure.quantity.Power;
24
25 import org.eclipse.jdt.annotation.NonNullByDefault;
26 import org.eclipse.jdt.annotation.Nullable;
27 import org.openhab.binding.sonnen.internal.communication.SonnenJSONCommunication;
28 import org.openhab.binding.sonnen.internal.communication.SonnenJsonDataDTO;
29 import org.openhab.core.library.types.OnOffType;
30 import org.openhab.core.library.types.QuantityType;
31 import org.openhab.core.library.unit.Units;
32 import org.openhab.core.thing.Channel;
33 import org.openhab.core.thing.ChannelUID;
34 import org.openhab.core.thing.Thing;
35 import org.openhab.core.thing.ThingStatus;
36 import org.openhab.core.thing.ThingStatusDetail;
37 import org.openhab.core.thing.binding.BaseThingHandler;
38 import org.openhab.core.types.Command;
39 import org.openhab.core.types.RefreshType;
40 import org.openhab.core.types.State;
41 import org.openhab.core.types.UnDefType;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44
45 /**
46  * The {@link SonnenHandler} is responsible for handling commands, which are
47  * sent to one of the channels.
48  *
49  * @author Christian Feininger - Initial contribution
50  */
51 @NonNullByDefault
52 public class SonnenHandler extends BaseThingHandler {
53
54     private final Logger logger = LoggerFactory.getLogger(SonnenHandler.class);
55
56     private SonnenConfiguration config = new SonnenConfiguration();
57
58     private @Nullable ScheduledFuture<?> refreshJob;
59
60     private SonnenJSONCommunication serviceCommunication;
61
62     private boolean automaticRefreshing = false;
63
64     private Map<String, Boolean> linkedChannels = new HashMap<>();
65
66     public SonnenHandler(Thing thing) {
67         super(thing);
68         serviceCommunication = new SonnenJSONCommunication();
69     }
70
71     @Override
72     public void initialize() {
73         logger.debug("Initializing sonnen handler for thing {}", getThing().getUID());
74         config = getConfigAs(SonnenConfiguration.class);
75         if (config.refreshInterval < 0 || config.refreshInterval > 1000) {
76             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
77                     "Parameter 'refresh Rate' msut be in the range 0-1000!");
78             return;
79         }
80         if (config.hostIP == null) {
81             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "IP Address must be configured!");
82             return;
83         }
84
85         serviceCommunication.setConfig(config);
86         updateStatus(ThingStatus.UNKNOWN);
87         scheduler.submit(() -> {
88             if (updateBatteryData()) {
89                 for (Channel channel : getThing().getChannels()) {
90                     if (isLinked(channel.getUID().getId())) {
91                         channelLinked(channel.getUID());
92                     }
93                 }
94             }
95         });
96     }
97
98     /**
99      * Calls the service to update the battery data
100      *
101      * @return true if the update succeeded, false otherwise
102      */
103     private boolean updateBatteryData() {
104         String error = serviceCommunication.refreshBatteryConnection();
105         if (error.isEmpty()) {
106             if (!ThingStatus.ONLINE.equals(getThing().getStatus())) {
107                 updateStatus(ThingStatus.ONLINE);
108             }
109         } else {
110             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, error);
111         }
112         return error.isEmpty();
113     }
114
115     private void verifyLinkedChannel(String channelID) {
116         if (isLinked(channelID) && !linkedChannels.containsKey(channelID)) {
117             linkedChannels.put(channelID, true);
118         }
119     }
120
121     @Override
122     public void dispose() {
123         stopAutomaticRefresh();
124         linkedChannels.clear();
125         automaticRefreshing = false;
126     }
127
128     private void stopAutomaticRefresh() {
129         ScheduledFuture<?> job = refreshJob;
130         if (job != null) {
131             job.cancel(true);
132         }
133         refreshJob = null;
134     }
135
136     /**
137      * Start the job refreshing the oven status
138      */
139     private void startAutomaticRefresh() {
140         ScheduledFuture<?> job = refreshJob;
141         if (job == null || job.isCancelled()) {
142             refreshJob = scheduler.scheduleWithFixedDelay(this::refreshChannels, 0, config.refreshInterval,
143                     TimeUnit.SECONDS);
144         }
145     }
146
147     private void refreshChannels() {
148         updateBatteryData();
149         for (Channel channel : getThing().getChannels()) {
150             updateChannel(channel.getUID().getId());
151         }
152     }
153
154     @Override
155     public void channelLinked(ChannelUID channelUID) {
156         if (!automaticRefreshing) {
157             logger.debug("Start automatic refreshing");
158             startAutomaticRefresh();
159             automaticRefreshing = true;
160         }
161         verifyLinkedChannel(channelUID.getId());
162         updateChannel(channelUID.getId());
163     }
164
165     @Override
166     public void channelUnlinked(ChannelUID channelUID) {
167         linkedChannels.remove(channelUID.getId());
168         if (linkedChannels.isEmpty()) {
169             automaticRefreshing = false;
170             stopAutomaticRefresh();
171             logger.debug("Stop automatic refreshing");
172         }
173     }
174
175     private void updateChannel(String channelId) {
176         if (isLinked(channelId)) {
177             State state = null;
178             SonnenJsonDataDTO data = serviceCommunication.getBatteryData();
179             if (data != null) {
180                 switch (channelId) {
181                     case CHANNELBATTERYDISCHARGINGSTATE:
182                         update(OnOffType.from(data.isBatteryDischarging()), channelId);
183                         break;
184                     case CHANNELBATTERYCHARGINGSTATE:
185                         update(OnOffType.from(data.isBatteryCharging()), channelId);
186                         break;
187                     case CHANNELCONSUMPTION:
188                         state = new QuantityType<Power>(data.getConsumptionHouse(), Units.WATT);
189                         update(state, channelId);
190                         break;
191                     case CHANNELBATTERYDISCHARGING:
192                         state = new QuantityType<Power>(data.getbatteryCurrent() > 0 ? data.getbatteryCurrent() : 0,
193                                 Units.WATT);
194                         update(state, channelId);
195                         break;
196                     case CHANNELBATTERYCHARGING:
197                         state = new QuantityType<Power>(
198                                 data.getbatteryCurrent() <= 0 ? (data.getbatteryCurrent() * -1) : 0, Units.WATT);
199                         update(state, channelId);
200                         break;
201                     case CHANNELGRIDFEEDIN:
202                         state = new QuantityType<Power>(data.getGridValue() > 0 ? data.getGridValue() : 0, Units.WATT);
203                         update(state, channelId);
204                         break;
205                     case CHANNELGRIDCONSUMPTION:
206                         state = new QuantityType<Power>(data.getGridValue() <= 0 ? (data.getGridValue() * -1) : 0,
207                                 Units.WATT);
208                         update(state, channelId);
209                         break;
210                     case CHANNELSOLARPRODUCTION:
211                         state = new QuantityType<Power>(data.getSolarProduction(), Units.WATT);
212                         update(state, channelId);
213                         break;
214                     case CHANNELBATTERYLEVEL:
215                         state = new QuantityType<Dimensionless>(data.getBatteryChargingLevel(), Units.PERCENT);
216                         update(state, channelId);
217                         break;
218                     case CHANNELFLOWCONSUMPTIONBATTERYSTATE:
219                         update(OnOffType.from(data.isFlowConsumptionBattery()), channelId);
220                         break;
221                     case CHANNELFLOWCONSUMPTIONGRIDSTATE:
222                         update(OnOffType.from(data.isFlowConsumptionGrid()), channelId);
223                         break;
224                     case CHANNELFLOWCONSUMPTIONPRODUCTIONSTATE:
225                         update(OnOffType.from(data.isFlowConsumptionProduction()), channelId);
226                         break;
227                     case CHANNELFLOWGRIDBATTERYSTATE:
228                         update(OnOffType.from(data.isFlowGridBattery()), channelId);
229                         break;
230                     case CHANNELFLOWPRODUCTIONBATTERYSTATE:
231                         update(OnOffType.from(data.isFlowProductionBattery()), channelId);
232                         break;
233                     case CHANNELFLOWPRODUCTIONGRIDSTATE:
234                         update(OnOffType.from(data.isFlowProductionGrid()), channelId);
235                         break;
236                 }
237             } else {
238                 update(null, channelId);
239             }
240         }
241     }
242
243     /**
244      * Updates the State of the given channel
245      *
246      * @param state Given state
247      * @param channelId the refereed channelID
248      */
249     private void update(@Nullable State state, String channelId) {
250         logger.debug("Update channel {} with state {}", channelId, (state == null) ? "null" : state.toString());
251         updateState(channelId, state != null ? state : UnDefType.UNDEF);
252     }
253
254     @Override
255     public void handleCommand(ChannelUID channelUID, Command command) {
256         if (command == RefreshType.REFRESH) {
257             updateBatteryData();
258             updateChannel(channelUID.getId());
259         }
260     }
261 }