/**
* 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.persistence.caldav.internal;
import static org.openhab.persistence.caldav.internal.CaldavConfiguration.calendarId;
import static org.openhab.persistence.caldav.internal.CaldavConfiguration.duration;
import static org.openhab.persistence.caldav.internal.CaldavConfiguration.singleEvents;
import static org.openhab.persistence.caldav.internal.CaldavConfiguration.futureOffset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
import org.joda.time.DateTime;
import org.openhab.core.items.Item;
import org.openhab.core.items.ItemNotFoundException;
import org.openhab.core.items.ItemRegistry;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.OpenClosedType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.persistence.FilterCriteria;
import org.openhab.core.persistence.HistoricItem;
import org.openhab.core.persistence.PersistenceService;
import org.openhab.core.persistence.QueryablePersistenceService;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.openhab.io.caldav.CalDavEvent;
import org.openhab.io.caldav.CalDavLoader;
import org.openhab.io.caldav.CalDavQuery;
import org.openhab.io.caldav.EventUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This is a {@link PersistenceService} implementation using calDAV.
*
* @author Robert Delbrück
* @since 1.8.0
*/
public class CaldavPersistenceService implements QueryablePersistenceService {
private static final Logger logger = LoggerFactory.getLogger(CaldavPersistenceService.class);
private static final String SERVICE_NAME = "caldav";
private CalDavLoader calDavLoader;
private ItemRegistry itemRegistry;
@Override
public String getName() {
return SERVICE_NAME;
}
public void setCalDavLoader(CalDavLoader calDavLoader) {
this.calDavLoader = calDavLoader;
}
public void unsetCalDavLoader() {
this.calDavLoader = null;
}
public void activate() {
}
public void deactivate() {
}
public void setItemRegistry(ItemRegistry itemRegistry) {
this.itemRegistry = itemRegistry;
}
public void unsetItemRegistry(ItemRegistry itemRegistry) {
this.itemRegistry = null;
}
@Override
public void store(Item item) {
store(item, null);
}
private CaldavItem findLastOn(String alias, State state) {
final FilterCriteria filter = new FilterCriteria();
filter.setEndDate(new Date());
filter.setItemName(alias);
filter.setOrdering(FilterCriteria.Ordering.DESCENDING);
filter.setPageSize(1);
final Iterable<HistoricItem> query = this.query(filter);
final Iterator<HistoricItem> iterator = query.iterator();
if (iterator.hasNext()) {
CaldavItem caldavItem = (CaldavItem) iterator.next();
if (!isOff(caldavItem.getState())) {
return caldavItem;
}
}
return null;
}
@Override
public void store(Item item, String alias) {
if (item.getState() instanceof UnDefType) {
return;
}
DateTime dateOffset = DateTime.now().plusDays(futureOffset);
if (alias == null) {
alias = item.getName();
}
State state = item.getState();
logger.trace("persisting item: {}", item);
if (!singleEvents) {
// try to create events with correct ON OFF duration
final CaldavItem lastOn = this.findLastOn(alias, state);
if (lastOn != null) {
if (isOff(item.getState())) {
CalDavEvent event = lastOn.getEvent();
event.setLastChanged(DateTime.now());
String offContent = EventUtils.createEnd(alias, state);
event.setContent(event.getContent() + "\n" + offContent);
event.setEnd(dateOffset);
logger.debug("existing event found, updated for persistence: {}", event);
this.calDavLoader.addEvent(event);
} else {
CalDavEvent event = lastOn.getEvent();
event.setLastChanged(dateOffset);
String offContent = EventUtils.createBetween(alias, state);
event.setContent(event.getContent() + "\n" + offContent);
logger.debug("existing event found, updated for persistence: {}", event);
this.calDavLoader.addEvent(event);
}
return;
}
}
logger.debug("creating new event");
CalDavEvent event = new CalDavEvent();
final String id = UUID.randomUUID().toString();
event.setId(id);
event.setName(alias);
event.setLastChanged(DateTime.now());
event.setContent(EventUtils.createBegin(alias, state));
event.setStart(dateOffset);
event.setEnd(dateOffset.plusMinutes(duration));
event.setCalendarId(calendarId);
event.setFilename("openHAB-" + id);
logger.debug("new event for persistence created: {}", event);
this.calDavLoader.addEvent(event);
}
private boolean isOff(State state) {
if (state instanceof PercentType && state.equals(new PercentType(0))) {
return true;
} else if (state instanceof OnOffType && state.equals(OnOffType.OFF)) {
return true;
} else if (state instanceof OpenClosedType && state.equals(OpenClosedType.CLOSED)) {
return true;
}
return false;
}
@Override
public Iterable<HistoricItem> query(final FilterCriteria filter) {
List<CalDavEvent> events = calDavLoader.getEvents(new CalDavQuery(calendarId));
List<HistoricItem> outList = new ArrayList<HistoricItem>();
for (CalDavEvent calDavEvent : events) {
if (filter.getBeginDate() != null && calDavEvent.getEnd().toDate().before(filter.getBeginDate())) {
continue;
}
if (filter.getEndDate() != null && calDavEvent.getStart().toDate().after(filter.getEndDate())) {
continue;
}
Item item = null;
try {
item = this.itemRegistry.getItem(filter.getItemName());
} catch (ItemNotFoundException e) {
logger.error("item {} could not be found", filter.getItemName());
continue;
}
final List<EventUtils.EventContent> parseContent = EventUtils.parseContent(calDavEvent, item);
for (EventUtils.EventContent eventContent : parseContent) {
if (filter.getBeginDate() != null && eventContent.getTime().toDate().before(filter.getBeginDate())) {
continue;
}
if (filter.getEndDate() != null && eventContent.getTime().toDate().after(filter.getEndDate())) {
continue;
}
final State eventState = eventContent.getState();
if (filter.getState() != null && filter.getOperator() != null) {
switch (filter.getOperator()) {
case EQ: {
if (!filter.getState().equals(eventState)) {
continue;
}
break;
}
case NEQ: {
if (filter.getState().equals(eventState)) {
continue;
}
break;
}
case LTE: {
if (eventState instanceof DecimalType && filter.getState() instanceof DecimalType) {
if (((DecimalType) eventState).longValue() > ((DecimalType) filter.getState())
.longValue()) {
continue;
}
} else {
continue;
}
break;
}
case GTE: {
if (eventState instanceof DecimalType && filter.getState() instanceof DecimalType) {
if (((DecimalType) eventState).longValue() < ((DecimalType) filter.getState())
.longValue()) {
continue;
}
} else {
continue;
}
break;
}
case LT: {
if (eventState instanceof DecimalType && filter.getState() instanceof DecimalType) {
if (((DecimalType) eventState).longValue() >= ((DecimalType) filter.getState())
.longValue()) {
continue;
}
} else {
continue;
}
break;
}
case GT: {
if (eventState instanceof DecimalType && filter.getState() instanceof DecimalType) {
if (((DecimalType) eventState).longValue() <= ((DecimalType) filter.getState())
.longValue()) {
continue;
}
} else {
continue;
}
break;
}
}
}
// just filtered events are here...
final CaldavItem caldavItem = new CaldavItem(filter.getItemName(), eventState,
eventContent.getTime().toDate());
caldavItem.setEvent(calDavEvent);
outList.add(caldavItem);
}
}
Collections.sort(outList, new Comparator<HistoricItem>() {
@Override
public int compare(HistoricItem arg0, HistoricItem arg1) {
if (filter.getOrdering().equals(FilterCriteria.Ordering.ASCENDING)) {
return (int) (arg0.getTimestamp().getTime() - arg1.getTimestamp().getTime());
} else {
return (int) (arg1.getTimestamp().getTime() - arg0.getTimestamp().getTime());
}
}
});
if (outList.size() < filter.getPageNumber() * filter.getPageSize()) {
return Collections.emptyList();
}
outList = outList.subList(filter.getPageNumber() * filter.getPageSize(),
Math.min((filter.getPageNumber() * filter.getPageSize()) + filter.getPageSize(), outList.size()));
logger.trace("result size for query: {}", outList.size());
return outList;
}
}