/**
* 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.caldav_personal.internal;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.openhab.binding.caldav_personal.CalDavBindingProvider;
import org.openhab.binding.caldav_personal.internal.CalDavConfig.Type;
import org.openhab.core.binding.AbstractBinding;
import org.openhab.core.binding.BindingProvider;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.types.State;
import org.openhab.io.caldav.CalDavEvent;
import org.openhab.io.caldav.CalDavLoader;
import org.openhab.io.caldav.CalDavQuery;
import org.openhab.io.caldav.CalDavQuery.Sort;
import org.openhab.io.caldav.EventNotifier;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.cm.ManagedService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Maps events from calDAV to items. Can be used to show personal events in sitemaps.
*
* @author Robert Delbrück
* @since 1.8.0
*/
public class CalDavBinding extends AbstractBinding<CalDavBindingProvider> implements ManagedService, EventNotifier {
private static final String PARAM_HOME_IDENTIFIERS = "homeIdentifiers";
private static final String PARAM_USED_CALENDARS = "usedCalendars";
private static final DateTimeFormatter DF = DateTimeFormat.shortDateTime();
private static final DateTimeFormatter FORMATTER = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss");
private static final Logger logger = LoggerFactory.getLogger(CalDavBinding.class);
private CalDavLoader calDavLoader;
private List<String> calendars = new ArrayList<String>();
private List<String> homeIdentifier = new ArrayList<String>();
public CalDavBinding() {
}
public void setCalDavLoader(CalDavLoader calDavLoader) {
logger.debug("setting CalDavLoader: {}", calDavLoader != null);
this.calDavLoader = calDavLoader;
this.calDavLoader.addListener(this);
}
public void unsetCalDavLoader(CalDavLoader calDavLoader) {
this.calDavLoader.removeListener(this);
this.calDavLoader = null;
}
@Override
public void activate() {
logger.debug("CalDavBinding (personal) activated");
}
@Override
public void deactivate() {
if (this.calDavLoader != null) {
this.calDavLoader.removeListener(this);
}
}
protected void addBindingProvider(CalDavBindingProvider bindingProvider) {
super.addBindingProvider(bindingProvider);
}
protected void removeBindingProvider(CalDavBindingProvider bindingProvider) {
super.removeBindingProvider(bindingProvider);
}
/**
* {@inheritDoc}
*/
@Override
public void updated(Dictionary<String, ?> properties) throws ConfigurationException {
if (properties != null) {
logger.debug("loading configuration...");
String usedCalendars = (String) properties.get(PARAM_USED_CALENDARS);
if (usedCalendars != null) {
for (String cal : usedCalendars.split(",")) {
this.calendars.add(cal.trim());
}
}
String homeIdentifiers = (String) properties.get(PARAM_HOME_IDENTIFIERS);
if (homeIdentifiers != null) {
for (String homeIdent : homeIdentifiers.split(",")) {
this.homeIdentifier.add(homeIdent.trim().toLowerCase());
}
}
logger.debug("loading configuration done");
this.reloadCurrentLoadedEvents();
}
}
private void reloadCurrentLoadedEvents() {
try {
if (this.calDavLoader == null) {
return;
}
logger.trace("reloading events");
for (String calendarKey : this.calendars) {
calendarReloaded(calendarKey);
}
} catch (Exception e) {
logger.error("cannot load events", e);
}
}
@Override
public void allBindingsChanged(BindingProvider provider) {
this.updateItemsForEvent();
super.allBindingsChanged(provider);
}
@Override
public void bindingChanged(BindingProvider provider, String itemName) {
if (!(provider instanceof CalDavBindingProvider)) {
return;
}
CalDavConfig config = ((CalDavBindingProvider) provider).getConfig(itemName);
if (config == null) {
return;
}
final List<CalDavEvent> events = this.calDavLoader.getEvents(getQueryForConfig(config));
this.updateItem(itemName, config, events);
}
/**
* {@inheritDoc}
*/
@Override
public void eventRemoved(CalDavEvent event) {
}
/**
* {@inheritDoc}
*/
@Override
public void eventLoaded(CalDavEvent event) {
}
/**
* {@inheritDoc}
*/
@Override
public void eventBegins(CalDavEvent event) {
if (!calendars.contains(event.getCalendarId())) {
return;
}
if (event.getStart().isBeforeNow()) {
return;
}
logger.debug("adding event to map: {}", event.getShortName());
this.updateItemsForEvent();
}
/**
* {@inheritDoc}
*/
@Override
public void eventEnds(CalDavEvent event) {
if (!calendars.contains(event.getCalendarId())) {
return;
}
logger.debug("remove event from map: {}", event.getShortName());
this.updateItemsForEvent();
}
@Override
public void calendarReloaded(String calendarId) {
if (!calendars.contains(calendarId)) {
return;
}
logger.debug("calendar reloaded: {}", calendarId);
this.updateItemsForEvent();
}
private void updateItemsForEvent() {
CalDavBindingProvider bindingProvider = null;
for (CalDavBindingProvider bindingProvider_ : this.providers) {
bindingProvider = bindingProvider_;
}
if (bindingProvider == null) {
logger.error("no binding provider found");
return;
}
Map<Integer, List<CalDavEvent>> eventCache = new HashMap<Integer, List<CalDavEvent>>();
for (String item : bindingProvider.getItemNames()) {
CalDavConfig config = bindingProvider.getConfig(item);
List<CalDavEvent> events = eventCache.get(config.getUniqueEventListKey());
if (events == null && this.calDavLoader != null) {
CalDavQuery query = getQueryForConfig(config);
events = this.calDavLoader.getEvents(query);
eventCache.put(config.getUniqueEventListKey(), events);
}
this.updateItem(item, config, events);
}
}
private CalDavQuery getQueryForConfig(CalDavConfig config) {
CalDavQuery query = new CalDavQuery(config.getCalendar(), DateTime.now(), Sort.ASCENDING);
query.setFilterName(config.getFilterName());
query.setFilterCategory(config.getFilterCategory());
query.setFilterCategoryMatchesAny(config.getCategoriesFiltersAny());
return query;
}
private synchronized void updateItem(String itemName, CalDavConfig config, List<CalDavEvent> events) {
if (config.getType() == Type.PRESENCE) {
List<CalDavEvent> subList = getActiveEvents(events);
subList = this.removeWithMatchingPlace(subList);
if (subList.size() == 0) {
eventPublisher.sendCommand(itemName, OnOffType.OFF);
} else {
eventPublisher.sendCommand(itemName, OnOffType.ON);
}
} else {
List<CalDavEvent> subList = new ArrayList<CalDavEvent>();
if (config.getType() == Type.EVENT) {
subList = getAllEvents(events);
} else if (config.getType() == Type.ACTIVE) {
subList = getActiveEvents(events);
} else if (config.getType() == Type.UPCOMING) {
subList = getUpcomingEvents(events);
}
if (config.getEventNr() > subList.size()) {
logger.debug("no event found for {}, setting to UNDEF", itemName);
eventPublisher.postUpdate(itemName, org.openhab.core.types.UnDefType.UNDEF);
return;
}
logger.debug("found {} events for config: {}", subList.size(), config);
CalDavEvent event = subList.get(config.getEventNr() - 1);
logger.trace("found event {} for config {}", event.getShortName(), config);
State command = null;
switch (config.getValue()) {
case NAME:
command = new StringType(event.getName());
break;
case DESCRIPTION:
command = new StringType(event.getContent());
break;
case PLACE:
command = new StringType(event.getLocation());
break;
case START:
command = new DateTimeType(FORMATTER.print(event.getStart()));
break;
case END:
command = new DateTimeType(FORMATTER.print(event.getEnd()));
break;
case TIME:
String startEnd = DF.print(event.getStart()) + " - " + DF.print(event.getEnd());
command = new StringType(startEnd);
break;
case NAMEANDTIME:
String startEnd2 = DF.print(event.getStart()) + " - " + DF.print(event.getEnd());
String name = event.getName();
command = new StringType(name + " @ " + startEnd2);
}
logger.debug("sending command {} for item {}", command, itemName);
eventPublisher.postUpdate(itemName, command);
logger.trace("command {} successfully sent", command);
}
}
private List<CalDavEvent> getActiveEvents(List<CalDavEvent> events) {
List<CalDavEvent> subList = new ArrayList<CalDavEvent>();
for (CalDavEvent event : events) {
if (!(event.getStart().isBeforeNow() && event.getEnd().isAfterNow())) {
continue;
}
subList.add(event);
}
return subList;
}
private List<CalDavEvent> getUpcomingEvents(List<CalDavEvent> events) {
List<CalDavEvent> subList = new ArrayList<CalDavEvent>();
for (CalDavEvent event : events) {
if (event.getStart().isBeforeNow()) {
continue;
}
subList.add(event);
}
return subList;
}
private List<CalDavEvent> getAllEvents(List<CalDavEvent> events) {
List<CalDavEvent> subList = new ArrayList<CalDavEvent>();
for (CalDavEvent event : events) {
subList.add(event);
}
return subList;
}
private List<CalDavEvent> removeWithMatchingPlace(List<CalDavEvent> list) {
List<CalDavEvent> out = new ArrayList<CalDavEvent>();
for (CalDavEvent event : list) {
if (this.homeIdentifierMatch(event.getLocation())) {
continue;
}
out.add(event);
}
return out;
}
private boolean homeIdentifierMatch(String location) {
if (location == null) {
return false;
}
boolean match = this.homeIdentifier.contains(location.toLowerCase());
if (match) {
return true;
}
for (String homeIdentifier : this.homeIdentifier) {
if (location.contains(homeIdentifier)) {
return true;
}
}
return false;
}
}