]> git.basschouten.com Git - openhab-addons.git/blob
955300fdcc80781ae332abd320ad67974b9de2f1
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 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.groheondus.internal.handler;
14
15 import static org.openhab.binding.groheondus.internal.GroheOndusBindingConstants.*;
16
17 import java.io.IOException;
18 import java.math.BigDecimal;
19 import java.time.Instant;
20 import java.time.temporal.ChronoUnit;
21 import java.util.List;
22 import java.util.Optional;
23
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.grohe.ondus.api.OndusService;
27 import org.grohe.ondus.api.model.BaseApplianceCommand;
28 import org.grohe.ondus.api.model.BaseApplianceData;
29 import org.grohe.ondus.api.model.guard.Appliance;
30 import org.grohe.ondus.api.model.guard.ApplianceCommand;
31 import org.grohe.ondus.api.model.guard.ApplianceData;
32 import org.grohe.ondus.api.model.guard.ApplianceData.Data;
33 import org.grohe.ondus.api.model.guard.ApplianceData.Measurement;
34 import org.openhab.core.library.types.DecimalType;
35 import org.openhab.core.library.types.OnOffType;
36 import org.openhab.core.library.types.QuantityType;
37 import org.openhab.core.library.types.StringType;
38 import org.openhab.core.library.unit.SIUnits;
39 import org.openhab.core.library.unit.Units;
40 import org.openhab.core.thing.Channel;
41 import org.openhab.core.thing.ChannelUID;
42 import org.openhab.core.thing.Thing;
43 import org.openhab.core.thing.ThingStatus;
44 import org.openhab.core.thing.ThingStatusDetail;
45 import org.openhab.core.types.Command;
46 import org.openhab.core.types.State;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
49
50 /**
51  * @author Florian Schmidt and Arne Wohlert - Initial contribution
52  */
53 @NonNullByDefault
54 public class GroheOndusSenseGuardHandler<T, M> extends GroheOndusBaseHandler<Appliance, Data> {
55     private static final int MIN_API_TIMEFRAME_DAYS = 1;
56     private static final int MAX_API_TIMEFRAME_DAYS = 90;
57     private static final int DEFAULT_TIMEFRAME_DAYS = 1;
58
59     private final Logger logger = LoggerFactory.getLogger(GroheOndusSenseGuardHandler.class);
60
61     public GroheOndusSenseGuardHandler(Thing thing) {
62         super(thing, Appliance.TYPE);
63     }
64
65     @Override
66     protected int getPollingInterval(Appliance appliance) {
67         if (config.pollingInterval > 0) {
68             return config.pollingInterval;
69         }
70         return appliance.getConfig().getMeasurementTransmissionIntervall();
71     }
72
73     @Override
74     protected void updateChannel(ChannelUID channelUID, Appliance appliance, Data dataPoint) {
75         String channelId = channelUID.getIdWithoutGroup();
76         State newState;
77         switch (channelId) {
78             case CHANNEL_NAME:
79                 newState = new StringType(appliance.getName());
80                 break;
81             case CHANNEL_PRESSURE:
82                 newState = new QuantityType<>(getLastMeasurement(dataPoint).getPressure(), Units.BAR);
83                 break;
84             case CHANNEL_TEMPERATURE_GUARD:
85                 newState = new QuantityType<>(getLastMeasurement(dataPoint).getTemperatureGuard(), SIUnits.CELSIUS);
86                 break;
87             case CHANNEL_VALVE_OPEN:
88                 newState = getValveOpenType(appliance);
89                 break;
90             case CHANNEL_WATERCONSUMPTION:
91                 newState = sumWaterCosumption(dataPoint);
92                 break;
93             default:
94                 throw new IllegalArgumentException("Channel " + channelUID + " not supported.");
95         }
96         if (newState != null) {
97             updateState(channelUID, newState);
98         }
99     }
100
101     private DecimalType sumWaterCosumption(Data dataPoint) {
102         Double waterConsumption = dataPoint.getWithdrawals().stream()
103                 .mapToDouble(withdrawal -> withdrawal.getWaterconsumption()).sum();
104         return new DecimalType(waterConsumption);
105     }
106
107     private Measurement getLastMeasurement(Data dataPoint) {
108         List<Measurement> measurementList = dataPoint.getMeasurement();
109         return measurementList.isEmpty() ? new Measurement() : measurementList.get(measurementList.size() - 1);
110     }
111
112     @Nullable
113     private OnOffType getValveOpenType(Appliance appliance) {
114         OndusService service = getOndusService();
115         if (service == null) {
116             return null;
117         }
118         Optional<BaseApplianceCommand> commandOptional;
119         try {
120             commandOptional = service.applianceCommand(appliance);
121         } catch (IOException e) {
122             logger.debug("Could not get appliance command", e);
123             return null;
124         }
125         if (!commandOptional.isPresent()) {
126             return null;
127         }
128         if (commandOptional.get().getType() != Appliance.TYPE) {
129             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
130                     "Thing is not a GROHE SENSE Guard device.");
131             return null;
132         }
133         return ((ApplianceCommand) commandOptional.get()).getCommand().getValveOpen() ? OnOffType.ON : OnOffType.OFF;
134     }
135
136     @Override
137     protected Data getLastDataPoint(Appliance appliance) {
138         if (getOndusService() == null) {
139             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE,
140                     "No initialized OndusService available from bridge.");
141             return new Data();
142         }
143
144         ApplianceData applianceData = getApplianceData(appliance);
145         if (applianceData == null) {
146             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Could not load data from API.");
147             return new Data();
148         }
149         return applianceData.getData();
150     }
151
152     private @Nullable ApplianceData getApplianceData(Appliance appliance) {
153         Instant from = fromTime();
154         Instant to = Instant.now();
155
156         OndusService service = getOndusService();
157         if (service == null) {
158             return null;
159         }
160         try {
161             BaseApplianceData applianceData = service.applianceData(appliance, from, to).orElse(null);
162             if (applianceData.getType() != Appliance.TYPE) {
163                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
164                         "Thing is not a GROHE SENSE Guard device.");
165                 return null;
166             }
167             return (ApplianceData) applianceData;
168         } catch (IOException e) {
169             logger.debug("Could not load appliance data", e);
170         }
171         return null;
172     }
173
174     private Instant fromTime() {
175         Instant from = Instant.now().minus(DEFAULT_TIMEFRAME_DAYS, ChronoUnit.DAYS);
176         Channel waterconsumptionChannel = this.thing.getChannel(CHANNEL_WATERCONSUMPTION);
177         if (waterconsumptionChannel == null) {
178             return from;
179         }
180
181         Object timeframeConfig = waterconsumptionChannel.getConfiguration().get(CHANNEL_CONFIG_TIMEFRAME);
182         if (!(timeframeConfig instanceof BigDecimal)) {
183             return from;
184         }
185
186         int timeframe = ((BigDecimal) timeframeConfig).intValue();
187         if (timeframe < MIN_API_TIMEFRAME_DAYS && timeframe > MAX_API_TIMEFRAME_DAYS) {
188             logger.info(
189                     "timeframe configuration of waterconsumption channel needs to be a number between 1 to 90, got {}",
190                     timeframe);
191             return from;
192         }
193
194         return Instant.now().minus(timeframe, ChronoUnit.DAYS);
195     }
196
197     @Override
198     public void handleCommand(ChannelUID channelUID, Command command) {
199         if (!CHANNEL_VALVE_OPEN.equals(channelUID.getIdWithoutGroup())) {
200             return;
201         }
202         if (!(command instanceof OnOffType)) {
203             logger.debug("Invalid command received for channel. Expected OnOffType, received {}.",
204                     command.getClass().getName());
205             return;
206         }
207         OnOffType openClosedCommand = (OnOffType) command;
208         boolean openState = openClosedCommand == OnOffType.ON;
209
210         OndusService service = getOndusService();
211         if (service == null) {
212             return;
213         }
214         Appliance appliance = getAppliance(service);
215         if (appliance == null) {
216             return;
217         }
218         try {
219             service.setValveOpen(appliance, openState);
220             updateChannels();
221         } catch (IOException e) {
222             logger.debug("Could not update valve open state", e);
223         }
224     }
225 }