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.boschshc.internal.services;
15 import java.util.concurrent.ExecutionException;
16 import java.util.concurrent.TimeoutException;
17 import java.util.function.Consumer;
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;
28 import com.google.gson.JsonElement;
31 * Abstract implementation of a service that supports reading and writing its state using the same JSON message and the
34 * The endpoints of this service have the following URL structure:
37 * https://{IP}:8444/smarthome/devices/{deviceId}/services/{serviceName}/state
40 * The HTTP client of the bridge will use <code>GET</code> requests to retrieve the state and <code>PUT</code> requests
43 * The services of the devices and their official APIs can be found
44 * <a href="https://apidocs.bosch-smarthome.com/local/">here</a>.
46 * @author Christian Oeing - Initial contribution
47 * @author David Pace - Service abstraction
50 public abstract class BoschSHCService<TState extends BoschSHCServiceState> extends AbstractBoschSHCService {
52 protected final Logger logger = LoggerFactory.getLogger(BoschSHCService.class);
55 * Class of service state
57 private final Class<TState> stateClass;
60 * Function to call after receiving state updates from the device
62 private @Nullable Consumer<TState> stateUpdateListener;
67 * @param serviceName Unique name of the service.
68 * @param stateClass State class that this service uses for data transfers
71 protected BoschSHCService(String serviceName, Class<TState> stateClass) {
73 this.stateClass = stateClass;
77 * Initializes the service
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
84 public void initialize(BridgeHandler bridgeHandler, String deviceId,
85 @Nullable Consumer<TState> stateUpdateListener) {
86 super.initialize(bridgeHandler, deviceId);
87 this.stateUpdateListener = stateUpdateListener;
91 * Returns the class of the state this service provides.
93 * @return Class of the state this service provides.
95 public Class<TState> getStateClass() {
96 return this.stateClass;
100 * Requests the current state of the service and updates it.
102 * @throws ExecutionException
103 * @throws TimeoutException
104 * @throws InterruptedException
105 * @throws BoschSHCException
107 public void refreshState() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
109 TState state = this.getState();
111 this.onStateUpdate(state);
116 * Requests the current state of the device with the specified id.
118 * @return Current state of the device.
119 * @throws ExecutionException
120 * @throws TimeoutException
121 * @throws InterruptedException
122 * @throws BoschSHCException
124 public @Nullable TState getState()
125 throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
126 String deviceId = getDeviceId();
127 if (deviceId == null) {
130 BridgeHandler bridgeHandler = getBridgeHandler();
131 if (bridgeHandler == null) {
134 return bridgeHandler.getState(deviceId, getServiceName(), getStateClass());
138 * Sets the state of the device with the specified id.
140 * @param state State to set.
141 * @throws InterruptedException
142 * @throws ExecutionException
143 * @throws TimeoutException
145 public void setState(TState state) throws InterruptedException, TimeoutException, ExecutionException {
146 String deviceId = getDeviceId();
147 if (deviceId == null) {
150 BridgeHandler bridgeHandler = getBridgeHandler();
151 if (bridgeHandler == null) {
154 bridgeHandler.putState(deviceId, getServiceName(), state);
158 * A state update was received from the bridge
160 * @param stateData Current state of service. Serialized as JSON.
162 public void onStateUpdate(@Nullable JsonElement stateData) {
164 TState state = BoschSHCServiceState.fromJson(stateData, this.stateClass);
166 this.logger.warn("Received invalid, expected type {}", this.stateClass.getName());
169 this.onStateUpdate(state);
173 * A state update was received from the bridge.
175 * @param state Current state of service as an instance of the state class.
177 private void onStateUpdate(TState state) {
178 Consumer<TState> stateUpdateListener = this.stateUpdateListener;
179 if (stateUpdateListener != null) {
180 stateUpdateListener.accept(state);
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.
188 * @param command Command to handle
189 * @throws BoschSHCException If service can not handle command
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()));