/** * 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.sitewhere.internal; import java.util.ArrayList; import java.util.Dictionary; import java.util.Map; import java.util.Properties; import org.openhab.core.events.EventPublisher; import org.openhab.core.items.Item; import org.openhab.core.items.ItemRegistry; 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.OpenClosedType; import org.openhab.core.library.types.PercentType; import org.openhab.core.library.types.PointType; import org.openhab.core.library.types.StringType; 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.osgi.framework.BundleContext; import org.osgi.service.cm.ConfigurationException; import org.osgi.service.cm.ManagedService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.sitewhere.agent.Agent; import com.sitewhere.agent.IAgentConfiguration; import com.sitewhere.agent.SiteWhereAgentException; /** * Implementation of {@link PersistenceService} for interacting with a <a * href="http://www.sitewhere.org/">SiteWhere</a> server instance. This service * sends events to a device (specified by the defaultHardwareId configuration * parameter) so that SiteWhere can store and process them. It also takes * commands from SiteWhere and pushes them to the openHAB bus to allow two-way * communication. If the given hardware id is not already registered in * SiteWhere, a new device instance will be created based on the specification * token provided in the configuration. The default specification token * corresponds to the openHAB device type included in the SiteWhere sample data. * * @author Derek Adams */ public class SiteWherePersistenceService implements PersistenceService, QueryablePersistenceService, ManagedService { /** Static logger instance */ private static final Logger LOGGER = LoggerFactory.getLogger(SiteWherePersistenceService.class); /** Alert type indicator for a DateTimeType */ public static final String TYPE_OPENHAB_DATETIME = "openhab.datetime"; /** Alert type indicator for a OnOffType */ public static final String TYPE_OPENHAB_ONOFF = "openhab.onoff"; /** Alert type indicator for a OpenClosedType */ public static final String TYPE_OPENHAB_OPENCLOSED = "openhab.openclosed"; /** Alert type indicator for a StringType */ public static final String TYPE_OPENHAB_STRING = "openhab.string"; /** Service name */ private static final String SERVICE_NAME = "sitewhere"; /** SiteWhere MQTT agent */ private Agent agent; /** Command processor that interacts with SiteWhere */ private OpenHabCommandProcessor sitewhere; /** Configuration settings */ private SiteWhereConfiguration configuration; /** Reference to openHAB item registry */ protected ItemRegistry itemRegistry; /** Event publisher for openHAB */ private EventPublisher eventPublisher; /** Indicates if the service has been configured */ protected boolean configured = false; /** Indicates if SiteWhere agent is connected */ protected boolean agentConnected = false; /* * (non-Javadoc) * * @see org.openhab.core.persistence.PersistenceService#getName() */ @Override public String getName() { return SERVICE_NAME; } /** * OSGI setter for {@link EventPublisher}. * * @param publisher */ public void setEventPublisher(EventPublisher publisher) { this.eventPublisher = publisher; } /** * OSGI unsetter for {@link EventPublisher}. * * @param publisher */ public void unsetEventPublisher(EventPublisher publisher) { this.eventPublisher = null; } /** * Method called when bundle is activated. * * @param bundleContext * @param config */ public void activate(final BundleContext bundleContext, final Map<String, Object> config) { LOGGER.trace("Activate method called on persistence service."); if (!isConfigured()) { SiteWhereConfiguration configuration = new SiteWhereConfiguration(config, LOGGER); setConfiguration(configuration); try { if (validateSiteWhereAgent()) { setConfigured(true); } } catch (Throwable t) { LOGGER.error("Error connecting to SiteWhere.", t); } } } /** * Method called when bundle is deactivated. */ public void deactivate() { LOGGER.trace("Deactivate method called on persistence service."); setConfigured(false); } /** * Set reference to item registry. * * @param itemRegistry */ public void setItemRegistry(ItemRegistry itemRegistry) { this.itemRegistry = itemRegistry; LOGGER.trace("Item registry set for SiteWhere persistence."); } /** * Unset reference to item registry. * * @param itemRegistry */ public void unsetItemRegistry(ItemRegistry itemRegistry) { this.itemRegistry = null; } /** * Validate that the SiteWhere MQTT agent can connect. * * @return */ protected boolean validateSiteWhereAgent() { LOGGER.info("About to connect using SiteWhere Agent..."); LOGGER.info("Sending event to device with hardware id: " + getConfiguration().getDefaultHardwareId()); LOGGER.info( "If no device registered, using specification: " + getConfiguration().getDeviceSpecificationToken()); LOGGER.info("MQTT Host: " + getConfiguration().getConnection().getMqttHost()); LOGGER.info("MQTT Port: " + getConfiguration().getConnection().getMqttPort()); // Create properties used to configure the SiteWhere Java agent. Properties props = new Properties(); props.setProperty(IAgentConfiguration.COMMAND_PROCESSOR_CLASSNAME, ""); props.setProperty(IAgentConfiguration.DEVICE_SPECIFICATION_TOKEN, getConfiguration().getDeviceSpecificationToken()); props.setProperty(IAgentConfiguration.DEVICE_HARDWARE_ID, getConfiguration().getDefaultHardwareId()); props.setProperty(IAgentConfiguration.MQTT_HOSTNAME, getConfiguration().getConnection().getMqttHost()); props.setProperty(IAgentConfiguration.MQTT_PORT, String.valueOf(getConfiguration().getConnection().getMqttPort())); // Create the agent. this.agent = new Agent(); agent.load(props); // Create a processor that facilitates communication with SiteWhere. this.sitewhere = new OpenHabCommandProcessor(eventPublisher); try { agent.start(sitewhere); setAgentConnected(true); return true; } catch (SiteWhereAgentException e) { setAgentConnected(false); LOGGER.error("Unable to start SiteWhere MQTT agent.", e); return false; } } /* * (non-Javadoc) * * @see org.osgi.service.cm.ManagedService#updated(java.util.Dictionary) */ @Override public void updated(Dictionary<String, ?> config) throws ConfigurationException { } /* * (non-Javadoc) * * @see * org.openhab.core.persistence.PersistenceService#store(org.openhab.core * .items.Item) */ @Override public void store(Item item) { store(item, null); } /* * (non-Javadoc) * * @see * org.openhab.core.persistence.PersistenceService#store(org.openhab.core * .items.Item, java.lang.String) */ @Override public void store(Item item, String alias) { if (isAgentConnected()) { String hwid = getConfiguration().getDefaultHardwareId(); try { State state = item.getState(); if (state instanceof PercentType) { addDeviceMeasurement(hwid, item, ((PercentType) state).toBigDecimal().doubleValue()); } else if (state instanceof DecimalType) { addDeviceMeasurement(hwid, item, ((DecimalType) state).toBigDecimal().doubleValue()); } else if (state instanceof DateTimeType) { addDeviceAlert(hwid, item, (DateTimeType) state); } else if (state instanceof OnOffType) { addDeviceAlert(hwid, item, (OnOffType) state); } else if (state instanceof OpenClosedType) { addDeviceAlert(hwid, item, (OpenClosedType) state); } else if (state instanceof StringType) { addDeviceAlert(hwid, item, (StringType) state); } else if (state instanceof PointType) { addDeviceLocation(hwid, item, (PointType) state); } else { LOGGER.debug("Unable to store item of type: " + item.getState().getClass().getSimpleName()); return; } } catch (SiteWhereAgentException e) { LOGGER.warn("Unable to store item: " + item.getName(), e); } } } /** * Send a device measurement to SiteWhere. * * @param hardwareId * @param item * @param value * @throws SiteWhereAgentException */ protected void addDeviceMeasurement(String hardwareId, Item item, double value) throws SiteWhereAgentException { sitewhere.sendMeasurement(hardwareId, item.getName(), value, null); } /** * Send an alert for a {@link DateTimeType}. * * @param hardwareId * @param item * @param value * @throws SiteWhereAgentException */ protected void addDeviceAlert(String hardwareId, Item item, DateTimeType value) throws SiteWhereAgentException { addDeviceAlert(hardwareId, item, TYPE_OPENHAB_DATETIME, value.toString()); } /** * Send an alert for an {@link OnOffType}. * * @param hardwareId * @param item * @param value * @throws SiteWhereAgentException */ protected void addDeviceAlert(String hardwareId, Item item, OnOffType value) throws SiteWhereAgentException { addDeviceAlert(hardwareId, item, TYPE_OPENHAB_ONOFF, value.toString()); } /** * Send an alert for an {@link OpenClosedType}. * * @param hardwareId * @param item * @param value * @throws SiteWhereAgentException */ protected void addDeviceAlert(String hardwareId, Item item, OpenClosedType value) throws SiteWhereAgentException { addDeviceAlert(hardwareId, item, TYPE_OPENHAB_OPENCLOSED, value.toString()); } /** * Send an alert for a {@link StringType}. * * @param hardwareId * @param item * @param value * @throws SiteWhereAgentException */ protected void addDeviceAlert(String hardwareId, Item item, StringType value) throws SiteWhereAgentException { addDeviceAlert(hardwareId, item, TYPE_OPENHAB_STRING, value.toString()); } /** * Send a device alert to SiteWhere. * * @param hardwareId * @param item * @param type * @param value * @throws SiteWhereAgentException */ protected void addDeviceAlert(String hardwareId, Item item, String type, String value) throws SiteWhereAgentException { String habType = type + ":" + item.getName(); sitewhere.sendAlert(hardwareId, habType, value, null); } /** * Send a device location to SiteWhere. * * @param hardwareId * @param item * @param point * @throws SiteWhereAgentException */ protected void addDeviceLocation(String hardwareId, Item item, PointType point) throws SiteWhereAgentException { sitewhere.sendLocation(hardwareId, point.getLatitude().doubleValue(), point.getLongitude().doubleValue(), point.getAltitude().doubleValue(), null); } /* * (non-Javadoc) * * @see * org.openhab.core.persistence.QueryablePersistenceService#query(org.openhab * .core.persistence.FilterCriteria) */ @Override public Iterable<HistoricItem> query(FilterCriteria filter) { return new ArrayList<HistoricItem>(); } protected SiteWhereConfiguration getConfiguration() { return configuration; } protected void setConfiguration(SiteWhereConfiguration configuration) { this.configuration = configuration; } protected boolean isConfigured() { return configured; } protected void setConfigured(boolean configured) { this.configured = configured; } protected boolean isAgentConnected() { return agentConnected; } protected void setAgentConnected(boolean agentConnected) { this.agentConnected = agentConnected; } }