2 * Copyright (c) 2010-2021 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.groheondus.internal.handler;
15 import static org.openhab.binding.groheondus.internal.GroheOndusBindingConstants.*;
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;
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;
51 * @author Florian Schmidt and Arne Wohlert - Initial contribution
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;
59 private final Logger logger = LoggerFactory.getLogger(GroheOndusSenseGuardHandler.class);
61 public GroheOndusSenseGuardHandler(Thing thing) {
62 super(thing, Appliance.TYPE);
66 protected int getPollingInterval(Appliance appliance) {
67 if (config.pollingInterval > 0) {
68 return config.pollingInterval;
70 return appliance.getConfig().getMeasurementTransmissionIntervall();
74 protected void updateChannel(ChannelUID channelUID, Appliance appliance, Data dataPoint) {
75 String channelId = channelUID.getIdWithoutGroup();
79 newState = new StringType(appliance.getName());
81 case CHANNEL_PRESSURE:
82 newState = new QuantityType<>(getLastMeasurement(dataPoint).getPressure(), Units.BAR);
84 case CHANNEL_TEMPERATURE_GUARD:
85 newState = new QuantityType<>(getLastMeasurement(dataPoint).getTemperatureGuard(), SIUnits.CELSIUS);
87 case CHANNEL_VALVE_OPEN:
88 newState = getValveOpenType(appliance);
90 case CHANNEL_WATERCONSUMPTION:
91 newState = sumWaterCosumption(dataPoint);
94 throw new IllegalArgumentException("Channel " + channelUID + " not supported.");
96 if (newState != null) {
97 updateState(channelUID, newState);
101 private DecimalType sumWaterCosumption(Data dataPoint) {
102 Double waterConsumption = dataPoint.getWithdrawals().stream()
103 .mapToDouble(withdrawal -> withdrawal.getWaterconsumption()).sum();
104 return new DecimalType(waterConsumption);
107 private Measurement getLastMeasurement(Data dataPoint) {
108 List<Measurement> measurementList = dataPoint.getMeasurement();
109 return measurementList.isEmpty() ? new Measurement() : measurementList.get(measurementList.size() - 1);
113 private OnOffType getValveOpenType(Appliance appliance) {
114 OndusService service = getOndusService();
115 if (service == null) {
118 Optional<BaseApplianceCommand> commandOptional;
120 commandOptional = service.applianceCommand(appliance);
121 } catch (IOException e) {
122 logger.debug("Could not get appliance command", e);
125 if (!commandOptional.isPresent()) {
128 if (commandOptional.get().getType() != Appliance.TYPE) {
129 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
130 "Thing is not a GROHE SENSE Guard device.");
133 return ((ApplianceCommand) commandOptional.get()).getCommand().getValveOpen() ? OnOffType.ON : OnOffType.OFF;
137 protected Data getLastDataPoint(Appliance appliance) {
138 if (getOndusService() == null) {
139 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE,
140 "No initialized OndusService available from bridge.");
144 ApplianceData applianceData = getApplianceData(appliance);
145 if (applianceData == null) {
146 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Could not load data from API.");
149 return applianceData.getData();
152 private @Nullable ApplianceData getApplianceData(Appliance appliance) {
153 Instant from = fromTime();
154 Instant to = Instant.now();
156 OndusService service = getOndusService();
157 if (service == null) {
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.");
167 return (ApplianceData) applianceData;
168 } catch (IOException e) {
169 logger.debug("Could not load appliance data", e);
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) {
181 Object timeframeConfig = waterconsumptionChannel.getConfiguration().get(CHANNEL_CONFIG_TIMEFRAME);
182 if (!(timeframeConfig instanceof BigDecimal)) {
186 int timeframe = ((BigDecimal) timeframeConfig).intValue();
187 if (timeframe < MIN_API_TIMEFRAME_DAYS && timeframe > MAX_API_TIMEFRAME_DAYS) {
189 "timeframe configuration of waterconsumption channel needs to be a number between 1 to 90, got {}",
194 return Instant.now().minus(timeframe, ChronoUnit.DAYS);
198 public void handleCommand(ChannelUID channelUID, Command command) {
199 if (!CHANNEL_VALVE_OPEN.equals(channelUID.getIdWithoutGroup())) {
202 if (!(command instanceof OnOffType)) {
203 logger.debug("Invalid command received for channel. Expected OnOffType, received {}.",
204 command.getClass().getName());
207 OnOffType openClosedCommand = (OnOffType) command;
208 boolean openState = openClosedCommand == OnOffType.ON;
210 OndusService service = getOndusService();
211 if (service == null) {
214 Appliance appliance = getAppliance(service);
215 if (appliance == null) {
219 service.setValveOpen(appliance, openState);
221 } catch (IOException e) {
222 logger.debug("Could not update valve open state", e);