/**
* Copyright (c) 2010-2016 by the respective copyright holders.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.openhab.binding.netatmo.internal.camera;
import java.util.Calendar;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.openhab.binding.netatmo.NetatmoBindingProvider;
import org.openhab.binding.netatmo.internal.NetatmoException;
import org.openhab.binding.netatmo.internal.authentication.OAuthCredentials;
import org.openhab.binding.netatmo.internal.camera.GetHomeDataResponse.Camera;
import org.openhab.binding.netatmo.internal.camera.GetHomeDataResponse.Event;
import org.openhab.binding.netatmo.internal.camera.GetHomeDataResponse.Home;
import org.openhab.binding.netatmo.internal.camera.GetHomeDataResponse.Person;
import org.openhab.binding.netatmo.internal.messages.NetatmoError;
import org.openhab.core.events.EventPublisher;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.types.State;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Implement this class if you are going create an actively polling service
* like querying a Website/Device.
*
* @author Ing. Peter Weiss
* @since 1.9.0
*/
public class NetatmoCameraBinding {
private static final Logger logger = LoggerFactory.getLogger(NetatmoCameraBinding.class);
private final Map<String, Map<String, Person>> configuredHomeKnownPersons = new HashMap<String, Map<String, Person>>();
private final Map<String, Map<String, Person>> configuredHomeUnKnownPersons = new HashMap<String, Map<String, Person>>();
private final Map<String, Home> configuredHomeMap = new HashMap<String, Home>();
private final Map<String, Map<String, Camera>> configuredhomeCameras = new HashMap<String, Map<String, Camera>>();
/**
* Execute the weather binding from Netatmo Binding Class
*
* @param oauthCredentials
* @param providers
* @param eventPublisher
*/
public void execute(OAuthCredentials oauthCredentials, Collection<NetatmoBindingProvider> providers,
EventPublisher eventPublisher) {
logger.debug("Querying Netatmo camera API");
try {
oauthCredentials.refreshAccessToken();
configuredHomeKnownPersons.clear();
configuredHomeUnKnownPersons.clear();
configuredHomeMap.clear();
configuredhomeCameras.clear();
processGetHomeData(oauthCredentials, providers, eventPublisher);
for (final NetatmoBindingProvider provider : providers) {
for (final String itemName : provider.getItemNames()) {
final String homeId = provider.getHomeId(itemName);
final NetatmoCameraAttributes attribute = provider.getAttribute(itemName);
State state = null;
if (homeId != null) {
final String personId = provider.getPersonId(itemName);
final String cameraId = provider.getCameraId(itemName);
if (personId != null) {
if ("UNKNOWN".equals(personId)) {
final StringBuilder message = new StringBuilder();
int i = 0;
switch (attribute) {
case HOME_UNKNWOWN_HOME_COUNT:
if (configuredHomeUnKnownPersons.containsKey(homeId)) {
for (Entry<String, Person> entry : configuredHomeUnKnownPersons.get(homeId)
.entrySet()) {
final Person person = entry.getValue();
if (!person.getOut_of_sight()) {
i++;
}
}
}
state = new DecimalType(i);
break;
case HOME_UNKNWOWN_AWAY_COUNT:
if (configuredHomeUnKnownPersons.containsKey(homeId)) {
for (Entry<String, Person> entry : configuredHomeUnKnownPersons.get(homeId)
.entrySet()) {
final Person person = entry.getValue();
if (person.getOut_of_sight()) {
i++;
}
}
}
state = new DecimalType(i);
break;
case HOME_UNKNWOWN_OUTOFSIGHT_LIST:
if (configuredHomeUnKnownPersons.containsKey(homeId)) {
for (Entry<String, Person> entry : configuredHomeUnKnownPersons.get(homeId)
.entrySet()) {
final Person person = entry.getValue();
if (message.length() > 1) {
message.append("#");
}
message.append(person.getOut_of_sight());
}
}
state = new StringType(message.toString());
break;
case HOME_UNKNWOWN_LASTSEEN_LIST:
if (configuredHomeUnKnownPersons.containsKey(homeId)) {
for (Entry<String, Person> entry : configuredHomeUnKnownPersons.get(homeId)
.entrySet()) {
final Person person = entry.getValue();
if (message.length() > 1) {
message.append("#");
}
final Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(person.getLastSeen().getTime() * 1000);
message.append(calendar);
}
}
state = new StringType(message.toString());
break;
case HOME_UNKNWOWN_FACE_ID_LIST:
if (configuredHomeUnKnownPersons.containsKey(homeId)) {
for (Entry<String, Person> entry : configuredHomeUnKnownPersons.get(homeId)
.entrySet()) {
final Person person = entry.getValue();
if (message.length() > 1) {
message.append("#");
}
message.append(person.getFace().getId());
}
}
state = new StringType(message.toString());
break;
case HOME_UNKNWOWN_FACE_KEY_LIST:
if (configuredHomeUnKnownPersons.containsKey(homeId)) {
for (Entry<String, Person> entry : configuredHomeUnKnownPersons.get(homeId)
.entrySet()) {
final Person person = entry.getValue();
if (message.length() > 1) {
message.append("#");
}
message.append(person.getFace().getKey());
}
}
state = new StringType(message.toString());
break;
default:
break;
}
} else {
Person person = null;
if (configuredHomeKnownPersons.containsKey(homeId)) {
person = configuredHomeKnownPersons.get(homeId).get(personId);
}
if (person != null) {
switch (attribute) {
case HOME_PERSON_OUTOFSIGHT:
if (person.getOut_of_sight()) {
state = OnOffType.OFF;
} else {
state = OnOffType.ON;
}
break;
case HOME_PERSON_PSEUDO:
state = new StringType(person.getPseudo());
break;
case HOME_PERSON_LASTSEEN:
final Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(person.getLastSeen().getTime() * 1000);
state = new DateTimeType(calendar);
break;
case HOME_PERSON_FACE_ID:
state = new StringType(person.getFace().getId());
break;
case HOME_PERSON_FACE_KEY:
state = new StringType(person.getFace().getKey());
break;
default:
break;
}
}
}
} else if (cameraId != null) {
Camera camera = null;
if (configuredhomeCameras.containsKey(homeId)) {
camera = configuredhomeCameras.get(homeId).get(cameraId);
}
if (camera != null) {
switch (attribute) {
case HOME_CAMERA_NAME:
state = new StringType(camera.getName());
break;
case HOME_CAMERA_STATUS:
state = new StringType(camera.getStatus());
break;
case HOME_CAMERA_SD_STATUS:
state = new StringType(camera.getSd_status());
break;
case HOME_CAMERA_ALIM_STATUS:
state = new StringType(camera.getAlim_status());
break;
default:
break;
}
}
}
else {
Home home = configuredHomeMap.get(homeId);
if (home != null) {
switch (attribute) {
case HOME_NAME:
state = new StringType(home.getName());
break;
case HOME_PLACE_COUNTRY:
state = new StringType(home.getPlace().getCountry());
break;
case HOME_PLACE_TIMEZONE:
state = new StringType(home.getPlace().getTimezone());
break;
default:
break;
}
}
}
}
if (state != null) {
eventPublisher.postUpdate(itemName, state);
}
}
}
} catch (NetatmoException ne) {
logger.error(ne.getMessage());
}
}
/**
* Camera Home Implementation
*
* @param oauthCredentials
* @param providers
*/
private void processGetHomeData(OAuthCredentials oauthCredentials, Collection<NetatmoBindingProvider> providers,
EventPublisher eventPublisher) {
GetHomeDataRequest homeDataRequest = new GetHomeDataRequest(oauthCredentials.getAccessToken());
logger.debug("Request: {}", homeDataRequest);
GetHomeDataResponse homeDataResponse = homeDataRequest.execute();
logger.debug("Response: {}", homeDataResponse);
if (homeDataResponse.isError()) {
final NetatmoError error = homeDataResponse.getError();
if (error.isAccessTokenExpired() || error.isTokenNotVaid()) {
logger.debug("Token is expired or is not valid, refreshing: code = {} message = {}", error.getCode(),
error.getMessage());
oauthCredentials.refreshAccessToken();
execute(oauthCredentials, providers, eventPublisher);
} else {
logger.error("Error processing home list: code = {} message = {}", error.getCode(), error.getMessage());
throw new NetatmoException(error.getMessage());
}
return; // abort processing
} else {
processGetHomeDataResponse(homeDataResponse, providers);
}
}
/**
* Camera Home
*
*
* Processes an incoming {@link GetHomeDataResponse}.
* <p>
*
* @param providers
*/
private void processGetHomeDataResponse(final GetHomeDataResponse response,
Collection<NetatmoBindingProvider> providers) {
final Map<String, Home> homeMap = new HashMap<String, Home>();
final Map<String, Map<String, Person>> homeKnownPersons = new HashMap<String, Map<String, Person>>();
final Map<String, Map<String, Person>> homeUnKnownPersons = new HashMap<String, Map<String, Person>>();
final Map<String, Set<Event>> homeEvents = new HashMap<String, Set<Event>>();
final Map<String, Map<String, Camera>> homeCameras = new HashMap<String, Map<String, Camera>>();
// Add Homes to a HasMap
for (final Home home : response.getHomes()) {
final String homeId = home.getId();
homeMap.put(homeId, home);
logger.debug("Response Home: ID=" + home.getId() + " NAME=" + home.getName() + " PLACE="
+ home.getPlace().toString());
// Add Cameras of this home
for (final Camera camera : home.getCameras()) {
if (!homeCameras.containsKey(homeId)) {
homeCameras.put(homeId, new HashMap<String, Camera>());
}
homeCameras.get(homeId).put(camera.getId(), camera);
logger.debug("Response Camera: ID=" + camera.getId() + " STATUS=" + camera.getStatus() + " SDSTATUS="
+ camera.getSd_status() + " ALIMSTATUS=" + camera.getAlim_status());
}
// Add Persons that have been seen in the house
for (final Person person : home.getPersons()) {
if (!homeKnownPersons.containsKey(homeId)) {
homeKnownPersons.put(homeId, new HashMap<String, Person>());
}
if (!homeUnKnownPersons.containsKey(homeId)) {
homeUnKnownPersons.put(homeId, new HashMap<String, Person>());
homeUnKnownPersons.get(homeId).put("UNKNOWN", null);
}
String pseudo = person.getPseudo();
if (pseudo != null) {
homeKnownPersons.get(homeId).put(person.getId(), person);
logger.debug("Response Known Person: ID=" + person.getId() + " (" + pseudo + ") AWAY="
+ person.getOut_of_sight().toString() + " LASTSEEN="
+ (person.getLastSeen() != null ? person.getLastSeen().toString() : "unknown"));
} else {
homeUnKnownPersons.get(homeId).put(person.getId(), person);
if (!configuredHomeUnKnownPersons.containsKey(homeId)) {
configuredHomeUnKnownPersons.put(homeId, new HashMap<String, Person>());
}
configuredHomeUnKnownPersons.get(homeId).put(person.getId(), person);
logger.debug("Response UnKnown Person: ID=" + person.getId() + " AWAY="
+ person.getOut_of_sight().toString() + " LASTSEEN="
+ (person.getLastSeen() != null ? person.getLastSeen().toString() : "unknown"));
}
}
// Add Events that happened in this home
for (final Event event : home.getEvents()) {
if (!homeEvents.containsKey(homeId)) {
homeEvents.put(homeId, new HashSet<Event>());
}
homeEvents.get(homeId).add(event);
logger.debug("Response Event: ID=" + event.getId() + " TYPE=" + event.getType() + " MESSAGE="
+ event.getMessage() + " TIME="
+ (event.getTime() != null ? event.getTime().toString() : "unknown"));
}
}
// Remove all configured items from the maps
for (final NetatmoBindingProvider provider : providers) {
for (final String itemName : provider.getItemNames()) {
final String homeId = provider.getHomeId(itemName);
if (homeId != null) {
final String personId = provider.getPersonId(itemName);
final String cameraId = provider.getCameraId(itemName);
if (personId != null) {
if ("UNKNOWN".equals(personId)) {
if (homeUnKnownPersons.containsKey(homeId)
&& homeUnKnownPersons.get(homeId).containsKey(personId)) {
homeUnKnownPersons.get(homeId).remove(personId);
}
} else {
if (!configuredHomeKnownPersons.containsKey(homeId)) {
configuredHomeKnownPersons.put(homeId, new HashMap<String, Person>());
}
if (homeKnownPersons.containsKey(homeId)
&& homeKnownPersons.get(homeId).containsKey(personId)) {
configuredHomeKnownPersons.get(homeId).put(personId,
homeKnownPersons.get(homeId).get(personId));
homeKnownPersons.get(homeId).remove(personId);
}
}
} else if (cameraId != null) {
if (!configuredhomeCameras.containsKey(homeId)) {
configuredhomeCameras.put(homeId, new HashMap<String, Camera>());
}
if (homeCameras.containsKey(homeId) && homeCameras.get(homeId).containsKey(cameraId)) {
configuredhomeCameras.get(homeId).put(cameraId, homeCameras.get(homeId).get(cameraId));
homeCameras.get(homeId).remove(cameraId);
}
} else {
// configuredHomeMap.put(homeId, home);
if (homeMap.containsKey(homeId)) {
configuredHomeMap.put(homeId, homeMap.get(homeId));
homeMap.remove(homeId);
}
}
}
}
}
// Log all unconfigured measurements
final StringBuilder message = new StringBuilder();
for (Entry<String, Home> entry : homeMap.entrySet()) {
final String homeId = entry.getKey();
message.append("\t HOME: " + homeId + " (" + entry.getValue().getName() + ")\n");
}
for (Entry<String, Map<String, Person>> entry : homeKnownPersons.entrySet()) {
final String homeId = entry.getKey();
for (Entry<String, Person> entry2 : entry.getValue().entrySet()) {
final String personId = entry2.getKey();
message.append("\t PERSON: " + homeId + "#" + personId + " (" + entry2.getValue().getPseudo() + ")\n");
}
}
for (Entry<String, Map<String, Person>> entry : homeUnKnownPersons.entrySet()) {
final String homeId = entry.getKey();
if (entry.getValue().containsKey("UNKNOWN")) {
message.append("\t PERSON: " + homeId + "#UNKNOWN\n");
}
}
for (Entry<String, Map<String, Camera>> entry : homeCameras.entrySet()) {
final String homeId = entry.getKey();
for (Entry<String, Camera> entry2 : entry.getValue().entrySet()) {
final String cameraId = entry2.getKey();
message.append("\t CAMERA: " + homeId + "#" + cameraId + " (" + entry2.getValue().getName() + ")\n");
}
}
if (message.length() > 0) {
message.insert(0, "The following Items from Netatmo Camera are not yet configured:\n");
logger.info(message.toString());
}
}
}