]> git.basschouten.com Git - openhab-addons.git/blob
14e4defa393dee5a2a34d5ca62babbe0c78929b9
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2022 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.io.homekit.internal.accessories;
14
15 import static org.openhab.io.homekit.internal.HomekitCharacteristicType.ACTIVE_STATUS;
16 import static org.openhab.io.homekit.internal.HomekitCharacteristicType.INUSE_STATUS;
17 import static org.openhab.io.homekit.internal.HomekitCharacteristicType.REMAINING_DURATION;
18
19 import java.util.HashMap;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.concurrent.CompletableFuture;
23 import java.util.concurrent.Executors;
24 import java.util.concurrent.ScheduledExecutorService;
25 import java.util.concurrent.ScheduledFuture;
26 import java.util.concurrent.TimeUnit;
27
28 import org.eclipse.jdt.annotation.Nullable;
29 import org.openhab.core.items.GenericItem;
30 import org.openhab.core.library.items.SwitchItem;
31 import org.openhab.core.library.types.DecimalType;
32 import org.openhab.core.library.types.OnOffType;
33 import org.openhab.core.types.RefreshType;
34 import org.openhab.io.homekit.internal.HomekitAccessoryUpdater;
35 import org.openhab.io.homekit.internal.HomekitCharacteristicType;
36 import org.openhab.io.homekit.internal.HomekitSettings;
37 import org.openhab.io.homekit.internal.HomekitTaggedItem;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40
41 import io.github.hapjava.accessories.ValveAccessory;
42 import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback;
43 import io.github.hapjava.characteristics.impl.common.ActiveEnum;
44 import io.github.hapjava.characteristics.impl.common.InUseEnum;
45 import io.github.hapjava.characteristics.impl.valve.RemainingDurationCharacteristic;
46 import io.github.hapjava.characteristics.impl.valve.ValveTypeEnum;
47 import io.github.hapjava.services.impl.ValveService;
48
49 /**
50  *
51  * @author Tim Harper - Initial contribution
52  * @author Eugen Freiter - timer implementation
53  */
54 public class HomekitValveImpl extends AbstractHomekitAccessoryImpl implements ValveAccessory {
55     private final Logger logger = LoggerFactory.getLogger(HomekitValveImpl.class);
56     private static final String CONFIG_VALVE_TYPE = "homekitValveType";
57     public static final String CONFIG_DEFAULT_DURATION = "homekitDefaultDuration";
58     private static final String CONFIG_TIMER = "homekitTimer";
59
60     private static final Map<String, ValveTypeEnum> CONFIG_VALVE_TYPE_MAPPING = new HashMap<String, ValveTypeEnum>() {
61         {
62             put("GENERIC", ValveTypeEnum.GENERIC);
63             put("IRRIGATION", ValveTypeEnum.IRRIGATION);
64             put("SHOWER", ValveTypeEnum.SHOWER);
65             put("FAUCET", ValveTypeEnum.WATER_FAUCET);
66         }
67     };
68     private final BooleanItemReader inUseReader;
69     private final BooleanItemReader activeReader;
70     private final ScheduledExecutorService timerService = Executors.newSingleThreadScheduledExecutor();
71     private ScheduledFuture<?> valveTimer;
72     private final boolean homekitTimer;
73
74     public HomekitValveImpl(HomekitTaggedItem taggedItem, List<HomekitTaggedItem> mandatoryCharacteristics,
75             HomekitAccessoryUpdater updater, HomekitSettings settings) throws IncompleteAccessoryException {
76         super(taggedItem, mandatoryCharacteristics, updater, settings);
77         inUseReader = createBooleanReader(INUSE_STATUS);
78         activeReader = createBooleanReader(ACTIVE_STATUS);
79         ValveService service = new ValveService(this);
80         getServices().add(service);
81         final String timerConfig = getAccessoryConfiguration(CONFIG_TIMER, "");
82         homekitTimer = timerConfig.equalsIgnoreCase("yes") || timerConfig.equalsIgnoreCase("true");
83         if (homekitTimer) {
84             addRemainingDurationCharacteristic(taggedItem, updater, service);
85         }
86     }
87
88     private void addRemainingDurationCharacteristic(HomekitTaggedItem taggedItem, HomekitAccessoryUpdater updater,
89             ValveService service) {
90         logger.trace("addRemainingDurationCharacteristic for {}", taggedItem);
91         service.addOptionalCharacteristic(new RemainingDurationCharacteristic(() -> {
92             int remainingTime = 0;
93             ScheduledFuture<?> future = valveTimer;
94             if (future != null && !future.isDone()) {
95                 remainingTime = Math.toIntExact(future.getDelay(TimeUnit.SECONDS));
96             }
97             return CompletableFuture.completedFuture(remainingTime);
98         }, HomekitCharacteristicFactory.getSubscriber(taggedItem, REMAINING_DURATION, updater),
99                 HomekitCharacteristicFactory.getUnsubscriber(taggedItem, REMAINING_DURATION, updater)));
100     }
101
102     /**
103      * return duration set by home app at corresponding OH items. if ot set, then return the default duration from
104      * configuration.
105      * 
106      * @return duraion
107      */
108     private int getDuration() {
109         int duration = 0;
110         final @Nullable DecimalType durationState = getStateAs(HomekitCharacteristicType.DURATION, DecimalType.class);
111         if (durationState != null) {
112             duration = durationState.intValue();
113         }
114         return duration;
115     }
116
117     private void startTimer() {
118         int duration = getDuration();
119         logger.trace("start timer for duration {}", duration);
120         if (duration > 0) {
121             stopTimer();
122             valveTimer = timerService.schedule(() -> {
123                 logger.trace("valve timer is over. switching off the valve");
124                 switchOffValve();
125                 // let home app refresh the remaining duration, which is 0
126                 ((GenericItem) getRootAccessory().getItem()).send(RefreshType.REFRESH);
127             }, duration, TimeUnit.SECONDS);
128             logger.trace("started valve timer for {} seconds.", duration);
129         } else {
130             logger.debug("valve timer not started as duration = 0");
131         }
132     }
133
134     private void stopTimer() {
135         ScheduledFuture<?> future = valveTimer;
136         if (future != null && !future.isDone()) {
137             future.cancel(true);
138         }
139     }
140
141     @Override
142     public CompletableFuture<ActiveEnum> getValveActive() {
143         return CompletableFuture
144                 .completedFuture(this.activeReader.getValue() ? ActiveEnum.ACTIVE : ActiveEnum.INACTIVE);
145     }
146
147     @Override
148     public CompletableFuture<Void> setValveActive(ActiveEnum state) {
149         getItem(ACTIVE_STATUS, SwitchItem.class).ifPresent(item -> {
150             item.send(OnOffType.from(state == ActiveEnum.ACTIVE));
151             if (homekitTimer) {
152                 if ((state == ActiveEnum.ACTIVE)) {
153                     startTimer();
154                 } else {
155                     stopTimer();
156                 }
157                 // let home app refresh the remaining duration
158                 ((GenericItem) getRootAccessory().getItem()).send(RefreshType.REFRESH);
159             }
160         });
161         return CompletableFuture.completedFuture(null);
162     }
163
164     private void switchOffValve() {
165         getItem(ACTIVE_STATUS, SwitchItem.class).ifPresent(item -> item.send(OnOffType.OFF));
166     }
167
168     @Override
169     public void subscribeValveActive(HomekitCharacteristicChangeCallback callback) {
170         subscribe(ACTIVE_STATUS, callback);
171     }
172
173     @Override
174     public void unsubscribeValveActive() {
175         unsubscribe(ACTIVE_STATUS);
176     }
177
178     @Override
179     public CompletableFuture<InUseEnum> getValveInUse() {
180         return CompletableFuture.completedFuture(inUseReader.getValue() ? InUseEnum.IN_USE : InUseEnum.NOT_IN_USE);
181     }
182
183     @Override
184     public void subscribeValveInUse(HomekitCharacteristicChangeCallback callback) {
185         subscribe(INUSE_STATUS, callback);
186     }
187
188     @Override
189     public void unsubscribeValveInUse() {
190         unsubscribe(INUSE_STATUS);
191     }
192
193     @Override
194     public CompletableFuture<ValveTypeEnum> getValveType() {
195         final String valveType = getAccessoryConfiguration(CONFIG_VALVE_TYPE, "GENERIC");
196         ValveTypeEnum type = CONFIG_VALVE_TYPE_MAPPING.get(valveType.toUpperCase());
197         return CompletableFuture.completedFuture(type != null ? type : ValveTypeEnum.GENERIC);
198     }
199
200     @Override
201     public void subscribeValveType(HomekitCharacteristicChangeCallback callback) {
202         // nothing changes here
203     }
204
205     @Override
206     public void unsubscribeValveType() {
207         // nothing changes here
208     }
209 }