]> git.basschouten.com Git - openhab-addons.git/blob
773c85df08106be21b8c99b55e85a10bb707434f
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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.boschshc.internal.services;
14
15 import java.util.concurrent.ExecutionException;
16 import java.util.concurrent.TimeoutException;
17 import java.util.function.Consumer;
18
19 import org.eclipse.jdt.annotation.NonNullByDefault;
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.openhab.binding.boschshc.internal.devices.bridge.BridgeHandler;
22 import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException;
23 import org.openhab.binding.boschshc.internal.services.dto.BoschSHCServiceState;
24 import org.openhab.core.types.Command;
25 import org.slf4j.Logger;
26 import org.slf4j.LoggerFactory;
27
28 import com.google.gson.JsonElement;
29
30 /**
31  * Abstract implementation of a service that supports reading and writing its state using the same JSON message and the
32  * same endpoint.
33  * <p>
34  * The endpoints of this service have the following URL structure:
35  *
36  * <pre>
37  * https://{IP}:8444/smarthome/devices/{deviceId}/services/{serviceName}/state
38  * </pre>
39  *
40  * The HTTP client of the bridge will use <code>GET</code> requests to retrieve the state and <code>PUT</code> requests
41  * to set the state.
42  * <p>
43  * The services of the devices and their official APIs can be found
44  * <a href="https://apidocs.bosch-smarthome.com/local/">here</a>.
45  *
46  * @author Christian Oeing - Initial contribution
47  * @author David Pace - Service abstraction
48  */
49 @NonNullByDefault
50 public abstract class BoschSHCService<TState extends BoschSHCServiceState> extends AbstractBoschSHCService {
51
52     protected final Logger logger = LoggerFactory.getLogger(BoschSHCService.class);
53
54     /**
55      * Class of service state
56      */
57     private final Class<TState> stateClass;
58
59     /**
60      * Function to call after receiving state updates from the device
61      */
62     private @Nullable Consumer<TState> stateUpdateListener;
63
64     /**
65      * Constructor
66      *
67      * @param serviceName Unique name of the service.
68      * @param stateClass State class that this service uses for data transfers
69      *            from/to the device.
70      */
71     protected BoschSHCService(String serviceName, Class<TState> stateClass) {
72         super(serviceName);
73         this.stateClass = stateClass;
74     }
75
76     /**
77      * Initializes the service
78      *
79      * @param bridgeHandler Bridge to use for communication from/to the device
80      * @param deviceId Id of device this service is for
81      * @param stateUpdateListener Function to call when a state update was received
82      *            from the device.
83      */
84     public void initialize(BridgeHandler bridgeHandler, String deviceId,
85             @Nullable Consumer<TState> stateUpdateListener) {
86         super.initialize(bridgeHandler, deviceId);
87         this.stateUpdateListener = stateUpdateListener;
88     }
89
90     /**
91      * Returns the class of the state this service provides.
92      *
93      * @return Class of the state this service provides.
94      */
95     public Class<TState> getStateClass() {
96         return this.stateClass;
97     }
98
99     /**
100      * Requests the current state of the service and updates it.
101      *
102      * @throws ExecutionException
103      * @throws TimeoutException
104      * @throws InterruptedException
105      * @throws BoschSHCException
106      */
107     public void refreshState() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
108         @Nullable
109         TState state = this.getState();
110         if (state != null) {
111             this.onStateUpdate(state);
112         }
113     }
114
115     /**
116      * Requests the current state of the device with the specified id.
117      *
118      * @return Current state of the device.
119      * @throws ExecutionException
120      * @throws TimeoutException
121      * @throws InterruptedException
122      * @throws BoschSHCException
123      */
124     public @Nullable TState getState()
125             throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
126         String deviceId = getDeviceId();
127         if (deviceId == null) {
128             return null;
129         }
130         BridgeHandler bridgeHandler = getBridgeHandler();
131         if (bridgeHandler == null) {
132             return null;
133         }
134         return bridgeHandler.getState(deviceId, getServiceName(), getStateClass());
135     }
136
137     /**
138      * Sets the state of the device with the specified id.
139      *
140      * @param state State to set.
141      * @throws InterruptedException
142      * @throws ExecutionException
143      * @throws TimeoutException
144      */
145     public void setState(TState state) throws InterruptedException, TimeoutException, ExecutionException {
146         String deviceId = getDeviceId();
147         if (deviceId == null) {
148             return;
149         }
150         BridgeHandler bridgeHandler = getBridgeHandler();
151         if (bridgeHandler == null) {
152             return;
153         }
154         bridgeHandler.putState(deviceId, getServiceName(), state);
155     }
156
157     /**
158      * A state update was received from the bridge
159      *
160      * @param stateData Current state of service. Serialized as JSON.
161      */
162     public void onStateUpdate(@Nullable JsonElement stateData) {
163         @Nullable
164         TState state = BoschSHCServiceState.fromJson(stateData, this.stateClass);
165         if (state == null) {
166             this.logger.warn("Received invalid, expected type {}", this.stateClass.getName());
167             return;
168         }
169         this.onStateUpdate(state);
170     }
171
172     /**
173      * A state update was received from the bridge.
174      *
175      * @param state Current state of service as an instance of the state class.
176      */
177     private void onStateUpdate(TState state) {
178         Consumer<TState> stateUpdateListener = this.stateUpdateListener;
179         if (stateUpdateListener != null) {
180             stateUpdateListener.accept(state);
181         }
182     }
183
184     /**
185      * Allows a service to handle a command and create a new state out of it.
186      * The new state still has to be set via setState.
187      *
188      * @param command Command to handle
189      * @throws BoschSHCException If service can not handle command
190      */
191     public TState handleCommand(Command command) throws BoschSHCException {
192         throw new BoschSHCException(
193                 String.format("%s: Can not handle command %s", this.getServiceName(), command.getClass().getName()));
194     }
195 }