import static org.openhab.binding.netatmo.internal.api.data.NetatmoConstants.*;
import static org.openhab.core.auth.oauth2client.internal.Keyword.*;
-import java.net.URI;
import java.util.List;
import java.util.Optional;
import java.util.Set;
*/
@NonNullByDefault
public class AuthenticationApi extends RestManager {
- public static final URI TOKEN_URI = getApiBaseBuilder(PATH_OAUTH, SUB_PATH_TOKEN).build();
- public static final URI AUTH_URI = getApiBaseBuilder(PATH_OAUTH, SUB_PATH_AUTHORIZE).build();
+ public static final String TOKEN_URI = getApiBaseBuilder(PATH_OAUTH, SUB_PATH_TOKEN).build().toString();
+ public static final String AUTH_URI = getApiBaseBuilder(PATH_OAUTH, SUB_PATH_AUTHORIZE).build().toString();
private List<Scope> grantedScope = List.of();
private @Nullable String authorization;
super(bridge, FeatureArea.NONE);
}
- public void setAccessToken(@Nullable String accessToken) {
- if (accessToken != null) {
- authorization = "Bearer " + accessToken;
- } else {
- authorization = null;
- }
- }
-
- public void setScope(String scope) {
- grantedScope = Stream.of(scope.split(" ")).map(s -> Scope.valueOf(s.toUpperCase())).toList();
+ public void setAccessToken(@Nullable String accessToken, String scope) {
+ authorization = accessToken != null ? "Bearer " + accessToken : null;
+ grantedScope = Stream.of(scope.toUpperCase().split(" ")).map(Scope::valueOf).toList();
}
public void dispose() {
import java.time.ZonedDateTime;
import java.util.Comparator;
import java.util.List;
+import java.util.function.Function;
+import java.util.stream.Collectors;
import javax.ws.rs.core.UriBuilder;
private List<HomeEvent> getEvents(@Nullable Object... params) throws NetatmoException {
UriBuilder uriBuilder = getApiUriBuilder(SUB_PATH_GET_EVENTS, params);
- BodyResponse<Home> body = get(uriBuilder, NAEventsDataResponse.class).getBody();
- if (body != null) {
- Home home = body.getElement();
- if (home != null) {
- return home.getEvents();
- }
+ if (get(uriBuilder, NAEventsDataResponse.class).getBody() instanceof BodyResponse<Home> body
+ && body.getElement() instanceof Home home) {
+ return home.getEvents();
}
throw new NetatmoException("home should not be null");
}
List<HomeEvent> events = getEvents(PARAM_HOME_ID, homeId);
// we have to rewind to the latest event just after freshestEventTime
- if (events.size() > 0) {
+ if (!events.isEmpty()) {
+ String oldestId = "";
HomeEvent oldestRetrieved = events.get(events.size() - 1);
- while (oldestRetrieved.getTime().isAfter(freshestEventTime)) {
- events.addAll(getEvents(PARAM_HOME_ID, homeId, PARAM_EVENT_ID, oldestRetrieved.getId()));
+ while (oldestRetrieved.getTime().isAfter(freshestEventTime) && !oldestId.equals(oldestRetrieved.getId())) {
+ oldestId = oldestRetrieved.getId();
+ events.addAll(getEvents(PARAM_HOME_ID, homeId, PARAM_EVENT_ID, oldestId, PARAM_SIZE, 300));
oldestRetrieved = events.get(events.size() - 1);
}
}
- // Remove unneeded events being before freshestEventTime
+ // Remove potential duplicates then unneeded events being before freshestEventTime
return events.stream().filter(event -> event.getTime().isAfter(freshestEventTime))
- .sorted(Comparator.comparing(HomeEvent::getTime).reversed()).toList();
+ .collect(Collectors.toConcurrentMap(HomeEvent::getId, Function.identity(), (p, q) -> p)).values()
+ .stream().sorted(Comparator.comparing(HomeEvent::getTime).reversed()).toList();
}
public List<HomeEvent> getPersonEvents(String homeId, String personId) throws NetatmoException {
public static final String PARAM_EVENT_ID = "event_id";
public static final String PARAM_SCHEDULE_ID = "schedule_id";
public static final String PARAM_OFFSET = "offset";
+ public static final String PARAM_SIZE = "size";
public static final String PARAM_GATEWAY_TYPE = "gateway_types";
public static final String PARAM_MODE = "mode";
public static final String PARAM_URL = "url";
import java.lang.reflect.Constructor;
import java.net.URI;
import java.nio.charset.StandardCharsets;
-import java.time.LocalDateTime;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Deque;
import java.util.concurrent.TimeoutException;
import java.util.function.BiFunction;
+import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriBuilder;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID;
-import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.ThingUID;
private final Logger logger = LoggerFactory.getLogger(ApiBridgeHandler.class);
private final AuthenticationApi connectApi = new AuthenticationApi(this);
private final Map<Class<? extends RestManager>, RestManager> managers = new HashMap<>();
- private final Deque<LocalDateTime> requestsTimestamps = new ArrayDeque<>(200);
+ private final Deque<Instant> requestsTimestamps = new ArrayDeque<>(200);
private final BindingConfiguration bindingConf;
private final HttpClient httpClient;
private final OAuthFactory oAuthFactory;
}
oAuthClientService = oAuthFactory
- .createOAuthClientService(this.getThing().getUID().getAsString(),
- AuthenticationApi.TOKEN_URI.toString(), AuthenticationApi.AUTH_URI.toString(),
- configuration.clientId, configuration.clientSecret, FeatureArea.ALL_SCOPES, false)
+ .createOAuthClientService(this.getThing().getUID().getAsString(), AuthenticationApi.TOKEN_URI,
+ AuthenticationApi.AUTH_URI, configuration.clientId, configuration.clientSecret,
+ FeatureArea.ALL_SCOPES, false)
.withGsonBuilder(new GsonBuilder().registerTypeAdapter(AccessTokenResponse.class,
new AccessTokenResponseDeserializer()));
return;
}
- logger.debug("Connecting to Netatmo API.");
+ logger.debug("Connected to Netatmo API.");
ApiHandlerConfiguration configuration = getConfiguration();
if (!configuration.webHookUrl.isBlank()) {
}
updateStatus(ThingStatus.ONLINE);
-
- getThing().getThings().stream().filter(Thing::isEnabled).map(Thing::getHandler)
- .filter(CommonInterface.class::isInstance).map(CommonInterface.class::cast)
- .forEach(CommonInterface::expireData);
}
private boolean authenticate(@Nullable String code, @Nullable String redirectUri) {
return false;
}
- connectApi.setAccessToken(accessTokenResponse.getAccessToken());
- connectApi.setScope(accessTokenResponse.getScope());
-
+ connectApi.setAccessToken(accessTokenResponse.getAccessToken(), accessTokenResponse.getScope());
return true;
}
grantServlet = Optional.of(servlet);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
ConfigurationLevel.REFRESH_TOKEN_NEEDED.message.formatted(servlet.getPath()));
+ connectApi.dispose();
}
public ApiHandlerConfiguration getConfiguration() {
InputStream stream = new ByteArrayInputStream(payload.getBytes(StandardCharsets.UTF_8));
try (InputStreamContentProvider inputStreamContentProvider = new InputStreamContentProvider(stream)) {
request.content(inputStreamContentProvider, contentType);
- request.header(HttpHeader.ACCEPT, "application/json");
+ request.header(HttpHeader.ACCEPT, MediaType.APPLICATION_JSON);
}
logger.trace(" -with payload: {} ", payload);
}
if (isLinked(requestCountChannelUID)) {
- LocalDateTime now = LocalDateTime.now();
- LocalDateTime oneHourAgo = now.minusHours(1);
+ Instant now = Instant.now();
requestsTimestamps.addLast(now);
+ Instant oneHourAgo = now.minus(1, ChronoUnit.HOURS);
while (requestsTimestamps.getFirst().isBefore(oneHourAgo)) {
requestsTimestamps.removeFirst();
}
updateState(requestCountChannelUID, new DecimalType(requestsTimestamps.size()));
}
+
logger.trace(" -with headers: {} ",
String.join(", ", request.getHeaders().stream().map(HttpField::toString).toList()));
ContentResponse response = request.send();
if (e.getStatusCode() == ServiceError.MAXIMUM_USAGE_REACHED) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "@text/maximum-usage-reached");
prepareReconnection(null, null);
+ } else if (e.getStatusCode() == ServiceError.INVALID_TOKEN_MISSING) {
+ startAuthorizationFlow();
}
throw e;
} catch (InterruptedException e) {
@Override
public void initialize() {
super.initialize();
- freshestEventTime = ZDT_REFERENCE;
securityId = handler.getThingConfigAs(HomeConfiguration.class).getIdForArea(FeatureArea.SECURITY);
}
protected List<NAObject> updateReadings(SecurityApi api) {
List<NAObject> result = new ArrayList<>();
try {
- for (HomeEvent event : api.getHomeEvents(securityId, freshestEventTime)) {
- HomeEvent previousEvent = eventBuffer.get(event.getCameraId());
- if (previousEvent == null || previousEvent.getTime().isBefore(event.getTime())) {
- eventBuffer.put(event.getCameraId(), event);
- }
- String personId = event.getPersonId();
- if (personId != null) {
- previousEvent = eventBuffer.get(personId);
- if (previousEvent == null || previousEvent.getTime().isBefore(event.getTime())) {
- eventBuffer.put(personId, event);
- }
+ api.getHomeEvents(securityId, freshestEventTime).stream().forEach(event -> {
+ bufferIfNewer(event.getCameraId(), event);
+ if (event.getPersonId() instanceof String personId) {
+ bufferIfNewer(personId, event);
}
if (event.getTime().isAfter(freshestEventTime)) {
freshestEventTime = event.getTime();
}
- }
+ });
} catch (NetatmoException e) {
logger.warn("Error retrieving last events for home '{}' : {}", securityId, e.getMessage());
}
return result;
}
+ private void bufferIfNewer(String id, HomeEvent event) {
+ HomeEvent previousEvent = eventBuffer.get(id);
+ if (previousEvent == null || previousEvent.getTime().isBefore(event.getTime())) {
+ eventBuffer.put(id, event);
+ }
+ }
+
public NAObjectMap<HomeDataPerson> getPersons() {
return persons;
}