]> git.basschouten.com Git - openhab-addons.git/blob
caa5d1085de706863d1021ab5e53c8602a238955
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 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.surepetcare.internal.handler;
14
15 import static org.openhab.binding.surepetcare.internal.SurePetcareConstants.*;
16
17 import java.time.LocalTime;
18 import java.time.format.DateTimeFormatter;
19 import java.time.format.DateTimeParseException;
20 import java.util.List;
21
22 import javax.measure.quantity.Mass;
23
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.openhab.binding.surepetcare.internal.SurePetcareAPIHelper;
26 import org.openhab.binding.surepetcare.internal.SurePetcareApiException;
27 import org.openhab.binding.surepetcare.internal.dto.SurePetcareDevice;
28 import org.openhab.binding.surepetcare.internal.dto.SurePetcareDeviceControl;
29 import org.openhab.binding.surepetcare.internal.dto.SurePetcareDeviceControl.Bowls.BowlSettings;
30 import org.openhab.binding.surepetcare.internal.dto.SurePetcareDeviceCurfew;
31 import org.openhab.core.library.types.DecimalType;
32 import org.openhab.core.library.types.OnOffType;
33 import org.openhab.core.library.types.QuantityType;
34 import org.openhab.core.library.types.StringType;
35 import org.openhab.core.library.unit.SIUnits;
36 import org.openhab.core.thing.ChannelUID;
37 import org.openhab.core.thing.Thing;
38 import org.openhab.core.types.Command;
39 import org.openhab.core.types.RefreshType;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42
43 /**
44  * The {@link SurePetcareDeviceHandler} is responsible for handling hubs and pet flaps created to represent Sure Petcare
45  * devices.
46  *
47  * @author Rene Scherer - Initial Contribution
48  * @author Holger Eisold - Added pet feeder status
49  */
50 @NonNullByDefault
51 public class SurePetcareDeviceHandler extends SurePetcareBaseObjectHandler {
52
53     private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm");
54
55     private static final float BATTERY_FULL_VOLTAGE = 4 * 1.5f; // 4x AA batteries of 1.5V each
56     private static final float BATTERY_EMPTY_VOLTAGE = 4.2f; // 4x AA batteries of 1.05V each
57     private static final float LOW_BATTERY_THRESHOLD = 4 * 1.1f;
58
59     private final Logger logger = LoggerFactory.getLogger(SurePetcareDeviceHandler.class);
60
61     public SurePetcareDeviceHandler(Thing thing, SurePetcareAPIHelper petcareAPI) {
62         super(thing, petcareAPI);
63         logger.debug("Created device handler for type {}", thing.getThingTypeUID().getAsString());
64     }
65
66     @Override
67     public void handleCommand(ChannelUID channelUID, Command command) {
68         if (command instanceof RefreshType) {
69             updateThingCache.getValue();
70         } else if (channelUID.getId().startsWith(DEVICE_CHANNEL_CURFEW_BASE)) {
71             handleCurfewCommand(channelUID, command);
72         } else {
73             switch (channelUID.getId()) {
74                 case DEVICE_CHANNEL_LOCKING_MODE:
75                     if (command instanceof StringType commandAsStringType) {
76                         synchronized (petcareAPI) {
77                             SurePetcareDevice device = petcareAPI.getDevice(thing.getUID().getId());
78                             if (device != null) {
79                                 String newLockingModeIdStr = commandAsStringType.toString();
80                                 try {
81                                     Integer newLockingModeId = Integer.valueOf(newLockingModeIdStr);
82                                     petcareAPI.setDeviceLockingMode(device, newLockingModeId);
83                                     updateState(DEVICE_CHANNEL_LOCKING_MODE,
84                                             new StringType(device.status.locking.modeId.toString()));
85                                 } catch (NumberFormatException e) {
86                                     logger.warn("Invalid locking mode: {}, ignoring command", newLockingModeIdStr);
87                                 } catch (SurePetcareApiException e) {
88                                     logger.warn(
89                                             "Error from SurePetcare API. Can't update locking mode {} for device {}",
90                                             newLockingModeIdStr, device, e);
91                                 }
92                             }
93                         }
94                     }
95                     break;
96                 case DEVICE_CHANNEL_LED_MODE:
97                     if (command instanceof StringType) {
98                         synchronized (petcareAPI) {
99                             SurePetcareDevice device = petcareAPI.getDevice(thing.getUID().getId());
100                             if (device != null) {
101                                 String newLedModeIdStr = command.toString();
102                                 try {
103                                     Integer newLedModeId = Integer.valueOf(newLedModeIdStr);
104                                     petcareAPI.setDeviceLedMode(device, newLedModeId);
105                                     updateState(DEVICE_CHANNEL_LOCKING_MODE,
106                                             new StringType(device.status.ledModeId.toString()));
107                                 } catch (NumberFormatException e) {
108                                     logger.warn("Invalid locking mode: {}, ignoring command", newLedModeIdStr);
109                                 } catch (SurePetcareApiException e) {
110                                     logger.warn("Error from SurePetcare API. Can't update LED mode {} for device {}",
111                                             newLedModeIdStr, device, e);
112                                 }
113                             }
114                         }
115                     }
116                     break;
117                 default:
118                     logger.warn("Update on unsupported channel {}, ignoring command", channelUID.getId());
119             }
120         }
121     }
122
123     @Override
124     protected void updateThing() {
125         SurePetcareDevice device = petcareAPI.getDevice(thing.getUID().getId());
126         if (device != null) {
127             logger.debug("Updating all thing channels for device : {}", device);
128             updateState(DEVICE_CHANNEL_ID, new DecimalType(device.id));
129             updateState(DEVICE_CHANNEL_NAME, new StringType(device.name));
130             updateState(DEVICE_CHANNEL_PRODUCT, new StringType(device.productId.toString()));
131             updateState(DEVICE_CHANNEL_ONLINE, OnOffType.from(device.status.online));
132
133             if (thing.getThingTypeUID().equals(THING_TYPE_HUB_DEVICE)) {
134                 updateState(DEVICE_CHANNEL_LED_MODE, new StringType(device.status.ledModeId.toString()));
135                 updateState(DEVICE_CHANNEL_PAIRING_MODE, new StringType(device.status.pairingModeId.toString()));
136             } else {
137                 float batVol = device.status.battery;
138                 updateState(DEVICE_CHANNEL_BATTERY_VOLTAGE, new DecimalType(batVol));
139                 updateState(DEVICE_CHANNEL_BATTERY_LEVEL, new DecimalType(Math.min(
140                         (batVol - BATTERY_EMPTY_VOLTAGE) / (BATTERY_FULL_VOLTAGE - BATTERY_EMPTY_VOLTAGE) * 100.0f,
141                         100.0f)));
142                 updateState(DEVICE_CHANNEL_LOW_BATTERY, OnOffType.from(batVol < LOW_BATTERY_THRESHOLD));
143                 updateState(DEVICE_CHANNEL_DEVICE_RSSI, new DecimalType(device.status.signal.deviceRssi));
144                 updateState(DEVICE_CHANNEL_HUB_RSSI, new DecimalType(device.status.signal.hubRssi));
145
146                 if (thing.getThingTypeUID().equals(THING_TYPE_FLAP_DEVICE)) {
147                     updateThingCurfews(device);
148                     updateState(DEVICE_CHANNEL_LOCKING_MODE, new StringType(device.status.locking.modeId.toString()));
149                 } else if (thing.getThingTypeUID().equals(THING_TYPE_FEEDER_DEVICE)) {
150                     int bowlId = device.control.bowls.bowlId;
151                     List<BowlSettings> bowlSettings = device.control.bowls.bowlSettings;
152                     int numBowls = bowlSettings.size();
153                     if (numBowls > 0) {
154                         if (bowlId == BOWL_ID_ONE_BOWL_USED) {
155                             updateState(DEVICE_CHANNEL_BOWLS_FOOD,
156                                     new StringType(bowlSettings.get(0).foodId.toString()));
157                             updateState(DEVICE_CHANNEL_BOWLS_TARGET, new QuantityType<Mass>(
158                                     device.control.bowls.bowlSettings.get(0).targetId, SIUnits.GRAM));
159                         } else if (bowlId == BOWL_ID_TWO_BOWLS_USED) {
160                             updateState(DEVICE_CHANNEL_BOWLS_FOOD_LEFT,
161                                     new StringType(bowlSettings.get(0).foodId.toString()));
162                             updateState(DEVICE_CHANNEL_BOWLS_TARGET_LEFT,
163                                     new QuantityType<Mass>(bowlSettings.get(0).targetId, SIUnits.GRAM));
164                             if (numBowls > 1) {
165                                 updateState(DEVICE_CHANNEL_BOWLS_FOOD_RIGHT,
166                                         new StringType(bowlSettings.get(1).foodId.toString()));
167                                 updateState(DEVICE_CHANNEL_BOWLS_TARGET_RIGHT,
168                                         new QuantityType<Mass>(bowlSettings.get(1).targetId, SIUnits.GRAM));
169                             }
170                         }
171                     }
172                     updateState(DEVICE_CHANNEL_BOWLS, new StringType(device.control.bowls.bowlId.toString()));
173                     updateState(DEVICE_CHANNEL_BOWLS_CLOSE_DELAY,
174                             new StringType(device.control.lid.closeDelayId.toString()));
175                     updateState(DEVICE_CHANNEL_BOWLS_TRAINING_MODE,
176                             new StringType(device.control.trainingModeId.toString()));
177                 } else {
178                     logger.warn("Unknown product type for device {}", thing.getUID().getAsString());
179                 }
180             }
181         }
182     }
183
184     private void updateThingCurfews(SurePetcareDevice device) {
185         for (int i = 0; i < FLAP_MAX_NUMBER_OF_CURFEWS; i++) {
186             SurePetcareDeviceCurfew curfew = device.control.curfewList.get(i);
187             logger.debug("updateThingCurfews - Updating device curfew: {}", curfew.toString());
188             updateState(DEVICE_CHANNEL_CURFEW_ENABLED + (i + 1), OnOffType.from(curfew.enabled));
189             updateState(DEVICE_CHANNEL_CURFEW_LOCK_TIME + (i + 1),
190                     new StringType(TIME_FORMATTER.format(curfew.lockTime)));
191             updateState(DEVICE_CHANNEL_CURFEW_UNLOCK_TIME + (i + 1),
192                     new StringType(TIME_FORMATTER.format(curfew.unlockTime)));
193         }
194     }
195
196     private void handleCurfewCommand(ChannelUID channelUID, Command command) {
197         String channelUIDBase = channelUID.getIdWithoutGroup().substring(0,
198                 channelUID.getIdWithoutGroup().length() - 1);
199         int slot = Integer.parseInt(channelUID.getAsString().substring(channelUID.getAsString().length() - 1));
200
201         synchronized (petcareAPI) {
202             SurePetcareDevice device = petcareAPI.getDevice(thing.getUID().getId());
203             if (device != null) {
204                 try {
205                     SurePetcareDeviceControl existingControl = device.control;
206                     SurePetcareDeviceCurfew curfew = existingControl.curfewList.get(slot - 1);
207                     boolean requiresUpdate = false;
208                     switch (channelUIDBase) {
209                         case DEVICE_CHANNEL_CURFEW_ENABLED:
210                             if (command instanceof OnOffType) {
211                                 if (curfew.enabled != command.equals(OnOffType.ON)) {
212                                     logger.debug("Enabling curfew slot: {}", slot);
213                                     requiresUpdate = true;
214                                 }
215                                 curfew.enabled = command.equals(OnOffType.ON);
216                             }
217                             break;
218                         case DEVICE_CHANNEL_CURFEW_LOCK_TIME:
219                             LocalTime newLockTime = LocalTime.parse(command.toString(), TIME_FORMATTER);
220                             if (!(curfew.lockTime.equals(newLockTime)) && curfew.enabled) {
221                                 logger.debug("Changing curfew slot {} lock time to: {}", slot, newLockTime);
222                                 requiresUpdate = true;
223                             }
224                             curfew.lockTime = newLockTime;
225
226                             break;
227                         case DEVICE_CHANNEL_CURFEW_UNLOCK_TIME:
228                             LocalTime newUnlockTime = LocalTime.parse(command.toString(), TIME_FORMATTER);
229                             if (!(curfew.unlockTime.equals(newUnlockTime)) && curfew.enabled) {
230                                 logger.debug("Changing curfew slot {} unlock time to: {}", slot, newUnlockTime);
231                                 requiresUpdate = true;
232                             }
233                             curfew.unlockTime = newUnlockTime;
234
235                             break;
236                         default:
237                             break;
238                     }
239
240                     if (requiresUpdate) {
241                         try {
242                             logger.debug("Updating curfews: {}", existingControl.curfewList.toString());
243                             petcareAPI.setCurfews(device, existingControl.curfewList);
244                             updateThingCurfews(device);
245                         } catch (SurePetcareApiException e) {
246                             logger.warn("Error from SurePetcare API. Can't update curfews for device {}: {}", device,
247                                     e.getMessage());
248                         }
249                     }
250                 } catch (DateTimeParseException e) {
251                     logger.warn("Incorrect curfew time format HH:mm: {}", e.getMessage());
252                 }
253             }
254
255         }
256     }
257 }