2 * Copyright (c) 2010-2023 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.surepetcare.internal.handler;
15 import static org.openhab.binding.surepetcare.internal.SurePetcareConstants.*;
17 import java.util.Collection;
20 import java.util.concurrent.ScheduledFuture;
21 import java.util.concurrent.TimeUnit;
23 import org.eclipse.jdt.annotation.NonNullByDefault;
24 import org.eclipse.jdt.annotation.Nullable;
25 import org.openhab.binding.surepetcare.internal.AuthenticationException;
26 import org.openhab.binding.surepetcare.internal.SurePetcareAPIHelper;
27 import org.openhab.binding.surepetcare.internal.discovery.SurePetcareDiscoveryService;
28 import org.openhab.binding.surepetcare.internal.dto.SurePetcareBridgeConfiguration;
29 import org.openhab.binding.surepetcare.internal.dto.SurePetcareDevice;
30 import org.openhab.binding.surepetcare.internal.dto.SurePetcareHousehold;
31 import org.openhab.binding.surepetcare.internal.dto.SurePetcarePet;
32 import org.openhab.core.library.types.OnOffType;
33 import org.openhab.core.thing.Bridge;
34 import org.openhab.core.thing.ChannelUID;
35 import org.openhab.core.thing.Thing;
36 import org.openhab.core.thing.ThingStatus;
37 import org.openhab.core.thing.ThingStatusDetail;
38 import org.openhab.core.thing.ThingUID;
39 import org.openhab.core.thing.binding.BaseBridgeHandler;
40 import org.openhab.core.thing.binding.ThingHandler;
41 import org.openhab.core.thing.binding.ThingHandlerService;
42 import org.openhab.core.types.Command;
43 import org.openhab.core.types.RefreshType;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
48 * The {@link SurePetcareBridgeHandler} is responsible for handling the bridge things created to use the Sure Petcare
49 * API. This way, the user credentials may be entered only once.
51 * It also spawns 2 background polling threads to update the more static data (topology) and the pet locations at
52 * different time intervals.
54 * @author Rene Scherer - Initial Contribution
57 public class SurePetcareBridgeHandler extends BaseBridgeHandler {
59 private final Logger logger = LoggerFactory.getLogger(SurePetcareBridgeHandler.class);
61 private final SurePetcareAPIHelper petcareAPI;
62 private @Nullable ScheduledFuture<?> topologyPollingJob;
63 private @Nullable ScheduledFuture<?> petStatusPollingJob;
65 public SurePetcareBridgeHandler(Bridge bridge, SurePetcareAPIHelper petcareAPI) {
67 this.petcareAPI = petcareAPI;
71 public void initialize() {
72 logger.debug("Initializing Sure Petcare bridge handler.");
73 SurePetcareBridgeConfiguration config = getConfigAs(SurePetcareBridgeConfiguration.class);
75 if (config.username != null && config.password != null) {
76 updateStatus(ThingStatus.UNKNOWN);
78 logger.debug("Login to SurePetcare API with username: {}", config.username);
79 petcareAPI.login(config.username, config.password);
80 logger.debug("Login successful, updating topology cache");
81 petcareAPI.updateTopologyCache();
82 logger.debug("Cache update successful, setting bridge status to ONLINE");
83 updateStatus(ThingStatus.ONLINE);
85 } catch (AuthenticationException e) {
86 logger.debug("Authentication exception during initializing", e);
87 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
88 "@text/offline.conf-error-authentication");
92 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
93 "@text/offline.conf-error-missing-username-or-password");
97 ScheduledFuture<?> job = topologyPollingJob;
98 if (job == null || job.isCancelled()) {
99 topologyPollingJob = scheduler.scheduleWithFixedDelay(() -> {
100 petcareAPI.updateTopologyCache();
102 }, config.refreshIntervalTopology, config.refreshIntervalTopology, TimeUnit.SECONDS);
103 logger.debug("Bridge topology polling job every {} seconds", config.refreshIntervalTopology);
106 job = petStatusPollingJob;
107 if (job == null || job.isCancelled()) {
108 petStatusPollingJob = scheduler.scheduleWithFixedDelay(this::pollAndUpdatePetStatus,
109 config.refreshIntervalStatus, config.refreshIntervalStatus, TimeUnit.SECONDS);
110 logger.debug("Pet status polling job every {} seconds", config.refreshIntervalStatus);
115 public Collection<Class<? extends ThingHandlerService>> getServices() {
116 return Set.of(SurePetcareDiscoveryService.class);
120 public void dispose() {
121 ScheduledFuture<?> job = topologyPollingJob;
122 if (job != null && !job.isCancelled()) {
124 topologyPollingJob = null;
125 logger.debug("Stopped topology background polling process");
127 job = petStatusPollingJob;
128 if (job != null && !job.isCancelled()) {
130 petStatusPollingJob = null;
131 logger.debug("Stopped pet status background polling process");
136 public void handleCommand(ChannelUID channelUID, Command command) {
137 if (command instanceof RefreshType) {
138 updateState(BRIDGE_CHANNEL_REFRESH, OnOffType.OFF);
140 switch (channelUID.getId()) {
141 case BRIDGE_CHANNEL_REFRESH:
142 if (OnOffType.ON.equals(command)) {
143 petcareAPI.updateTopologyCache();
145 updateState(BRIDGE_CHANNEL_REFRESH, OnOffType.OFF);
152 public ThingUID getUID() {
153 return thing.getUID();
156 public Iterable<SurePetcareHousehold> listHouseholds() {
157 return petcareAPI.getTopology().households;
160 public Iterable<SurePetcarePet> listPets() {
161 return petcareAPI.getTopology().pets;
164 public Iterable<SurePetcareDevice> listDevices() {
165 return petcareAPI.getTopology().devices;
168 protected synchronized void updateThings() {
169 logger.debug("Updating {} connected things", getThing().getThings().size());
170 // update existing things
171 for (Thing th : getThing().getThings()) {
172 String tid = th.getUID().getId();
173 Map<String, String> properties = null;
174 ThingHandler handler = th.getHandler();
175 if (handler instanceof SurePetcarePetHandler surePetcareHandler) {
176 surePetcareHandler.updateThing();
177 SurePetcarePet pet = petcareAPI.getTopology().getById(petcareAPI.getTopology().pets, tid);
179 properties = pet.getThingProperties();
181 } else if (handler instanceof SurePetcareHouseholdHandler surePetcareHouseholdHandler) {
182 surePetcareHouseholdHandler.updateThing();
183 SurePetcareHousehold household = petcareAPI.getTopology().getById(petcareAPI.getTopology().households,
185 if (household != null) {
186 properties = household.getThingProperties();
188 } else if (handler instanceof SurePetcareDeviceHandler surePetcareDevicedHandler) {
189 surePetcareDevicedHandler.updateThing();
190 SurePetcareDevice device = petcareAPI.getTopology().getById(petcareAPI.getTopology().devices, tid);
191 if (device != null) {
192 properties = device.getThingProperties();
195 if ((properties != null) && (handler instanceof SurePetcareBaseObjectHandler surePetcareBaseHandler)) {
196 surePetcareBaseHandler.updateProperties(properties);
201 private synchronized void pollAndUpdatePetStatus() {
202 petcareAPI.updatePetStatus();
203 for (Thing th : getThing().getThings()) {
204 if (th.getThingTypeUID().equals(THING_TYPE_PET)) {
205 ThingHandler handler = th.getHandler();
206 if (handler instanceof SurePetcarePetHandler surePetcarePetHandler) {
207 surePetcarePetHandler.updateThing();