/** * 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.astro.internal.bus; import java.io.PrintWriter; import java.io.StringWriter; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.Calendar; import java.util.HashMap; import java.util.Map; import org.apache.commons.lang.ObjectUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.WordUtils; import org.openhab.binding.astro.AstroBindingProvider; import org.openhab.binding.astro.internal.common.AstroContext; import org.openhab.binding.astro.internal.config.AstroBindingConfig; import org.openhab.binding.astro.internal.model.Planet; import org.openhab.binding.astro.internal.model.PlanetName; import org.openhab.binding.astro.internal.util.ItemIterator; import org.openhab.binding.astro.internal.util.ItemIterator.ItemIteratorCallback; import org.openhab.binding.astro.internal.util.PropertyUtils; import org.openhab.core.items.Item; 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.UnDefType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Publishes object properties to items. * * @author Gerhard Riegler * @since 1.6.0 */ public class PlanetPublisher { private static final Logger logger = LoggerFactory.getLogger(PlanetPublisher.class); private AstroContext context = AstroContext.getInstance(); private Map<String, Object> itemCache = new HashMap<String, Object>(); private static PlanetPublisher instance = null; private PlanetPublisher() { } /** * Returns the singleton instance of PlanetPublisher. */ public static synchronized PlanetPublisher getInstance() { if (instance == null) { instance = new PlanetPublisher(); } return instance; } /** * Clears the item cache. */ public void clear() { itemCache.clear(); } /** * Republish the state of the item. */ public void republishItem(String itemName) { AstroBindingConfig bindingConfig = null; for (AstroBindingProvider provider : context.getProviders()) { if (bindingConfig == null) { bindingConfig = provider.getBindingFor(itemName); } } if (bindingConfig == null) { logger.warn("Astro binding for item {} not found", itemName); } else { itemCache.remove(itemName); publish(bindingConfig.getPlanetName()); } } /** * Iterates through all items and publishes the states. */ public void publish(final PlanetName planetName) { final Planet planet = context.getPlanet(planetName); new ItemIterator().iterate(new ItemIteratorCallback() { @Override public void next(AstroBindingConfig bindingConfig, Item item) { if (planetName == bindingConfig.getPlanetName()) { try { Object value = PropertyUtils.getPropertyValue(planet, bindingConfig.getPlanetProperty()); if (!equalsCachedValue(value, item)) { publishValue(item, value, bindingConfig); itemCache.put(item.getName(), value); } } catch (Exception ex) { logger.warn(ex.getMessage(), ex); } } } }); } /** * Returns true, if the cached value is equal to the new value. */ private boolean equalsCachedValue(Object value, Item item) { int cachedValueHashCode = ObjectUtils.hashCode(itemCache.get(item.getName())); int valueHashCode = ObjectUtils.hashCode(value); return cachedValueHashCode == valueHashCode; } /** * Publishes the item with the value, if the item is a OnOffType and the * value is a calendar, a job is scheduled. */ private void publishValue(Item item, Object value, AstroBindingConfig bindingConfig) { if (value == null) { context.getEventPublisher().postUpdate(item.getName(), UnDefType.UNDEF); } else if (value instanceof Calendar) { Calendar calendar = (Calendar) value; if (bindingConfig.getOffset() != 0) { calendar = (Calendar) calendar.clone(); calendar.add(Calendar.MINUTE, bindingConfig.getOffset()); } if (item.getAcceptedDataTypes().contains(DateTimeType.class)) { context.getEventPublisher().postUpdate(item.getName(), new DateTimeType(calendar)); } else if (item.getAcceptedCommandTypes().contains(OnOffType.class)) { context.getJobScheduler().scheduleItem(calendar, item.getName()); } else { logger.warn("Unsupported type for item {}, only DateTimeType and OnOffType supported!", item.getName()); } } else if (value instanceof Number) { if (item.getAcceptedDataTypes().contains(DecimalType.class)) { BigDecimal decimalValue = new BigDecimal(value.toString()).setScale(2, RoundingMode.HALF_UP); context.getEventPublisher().postUpdate(item.getName(), new DecimalType(decimalValue)); } else if (value instanceof Long && item.getAcceptedDataTypes().contains(StringType.class) && "duration".equals(bindingConfig.getProperty())) { // special case, transforming duration to minute:second string context.getEventPublisher().postUpdate(item.getName(), new StringType(durationToString((Long) value))); } else { logger.warn("Unsupported type for item {}, only DecimalType supported!", item.getName()); } } else if (value instanceof String || value instanceof Enum) { if (item.getAcceptedDataTypes().contains(StringType.class)) { if (value instanceof Enum) { String enumValue = WordUtils.capitalizeFully(StringUtils.replace(value.toString(), "_", " ")); context.getEventPublisher().postUpdate(item.getName(), new StringType(enumValue)); } else { context.getEventPublisher().postUpdate(item.getName(), new StringType(value.toString())); } } else { logger.warn("Unsupported type for item {}, only String supported!", item.getName()); } } else { logger.warn("Unsupported value type {}", value.getClass().getSimpleName()); } } /** * Returns the minute:second string for the minutes number. */ private String durationToString(long minutes) { long hours = minutes / 60; minutes -= hours * 60; StringWriter sw = new StringWriter(); PrintWriter writer = new PrintWriter(sw); writer.printf("%02d:%02d", hours, minutes); writer.flush(); return sw.getBuffer().toString(); } }