/** * 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.mqtt.internal; import java.util.Dictionary; import org.apache.commons.lang.StringUtils; import org.openhab.binding.mqtt.MqttBindingProvider; import org.openhab.core.binding.AbstractBinding; import org.openhab.core.items.Item; import org.openhab.core.items.ItemNotFoundException; import org.openhab.core.items.ItemRegistry; import org.openhab.core.types.Command; import org.openhab.core.types.State; import org.openhab.io.transport.mqtt.MqttService; import org.osgi.service.cm.ConfigurationException; import org.osgi.service.cm.ManagedService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Binding to broadcasts all commands and/or states received on the OpenHab * event bus to predefined topics on an MQTT Broker. The binding can also * subscribe to state or command topics and publish all of these to the openHAB * event bus. * * @author Davy Vanherbergen * @since 1.3.0 */ public class MqttEventBusBinding extends AbstractBinding<MqttBindingProvider>implements ManagedService { private static final Logger logger = LoggerFactory.getLogger(MqttEventBusBinding.class); /** MqttService for sending/receiving messages **/ private MqttService mqttService; /** openHAB ItemRegistry for resolving item names to items **/ private ItemRegistry itemRegistry; /** Message producer for sending state messages to MQTT **/ private MqttMessagePublisher statePublisher; /** Message producer for sending command messages to MQTT **/ private MqttMessagePublisher commandPublisher; /** Message consumer for receiving state messages from MQTT **/ private MqttMessageSubscriber stateSubscriber; /** Message consumer for receiving state messages from MQTT **/ private MqttMessageSubscriber commandSubscriber; /** * Name of the broker defined in the openhab.cfg to use for sending messages * to **/ private String brokerName; @Override public void activate() { super.activate(); logger.debug("MQTT: Activating event bus binding."); } @Override public void deactivate() { if (StringUtils.isBlank(brokerName)) { return; } if (commandPublisher != null) { mqttService.unregisterMessageProducer(brokerName, commandPublisher); commandPublisher = null; } if (statePublisher != null) { mqttService.unregisterMessageProducer(brokerName, statePublisher); statePublisher = null; } if (commandSubscriber != null) { mqttService.unregisterMessageConsumer(brokerName, commandSubscriber); commandSubscriber = null; } if (stateSubscriber != null) { mqttService.unregisterMessageConsumer(brokerName, stateSubscriber); stateSubscriber = null; } } /** * Extract the item name from the topic. This should be the last part of the * topic string, as the topics are in the format of * /openHab/myItemName/command * * @param topic * from which to parse the item name. * @return item name or "unknown". */ private String getItemNameFromTopic(String topicDefinition, String actualTopic) { String itemName = "error-parsing-name-from-topic"; if (StringUtils.isEmpty(actualTopic) || actualTopic.indexOf('/') == -1) { return itemName; } String[] definitionParts = topicDefinition.split("/"); String[] actualParts = actualTopic.split("/"); for (int i = 0; i < definitionParts.length; i++) { if (definitionParts[i].equalsIgnoreCase("+")) { itemName = actualParts[i]; break; } } return itemName; } @Override public void receiveUpdate(String itemName, State newState) { if (newState == null || statePublisher == null || !statePublisher.isActivated()) { return; } statePublisher.publish(statePublisher.getTopic(itemName), newState.toString().getBytes()); } @Override public void receiveCommand(String itemName, Command command) { if (commandPublisher == null || command == null || !commandPublisher.isActivated()) { return; } commandPublisher.publish(commandPublisher.getTopic(itemName), command.toString().getBytes()); } /** * Setter for Declarative Services. Adds the MqttService instance. * * @param mqttService * Service. */ public void setMqttService(MqttService mqttService) { this.mqttService = mqttService; } /** * Unsetter for Declarative Services. * * @param mqttService * MqttService to remove. */ public void unsetMqttService(MqttService mqttService) { this.mqttService = null; } /** * Setter for the openHAB ItemRegistry. * * @param itemRegistry * the openHAB item registry. */ public void setItemRegistry(ItemRegistry itemRegistry) { this.itemRegistry = itemRegistry; } /** * Unsetter for the openHAB ItemRegistry. * * @param itemRegistry * itemRegistry to remove. */ public void unsetItemRegistry(ItemRegistry itemRegistry) { this.itemRegistry = null; } /** * Initialize publisher which publishes all openHAB commands to the given * MQTT topic. * * @param topic * to subscribe to. */ private void setupEventBusStatePublisher(String topic) { if (StringUtils.isBlank(topic)) { logger.trace("No topic defined for Event Bus State Publisher"); return; } try { logger.debug("Setting up Event Bus State Publisher for topic {}", topic); statePublisher = new MqttMessagePublisher(brokerName + ":" + topic + ":state:*:default"); mqttService.registerMessageProducer(brokerName, statePublisher); } catch (Exception e) { logger.error("Could not create event bus state publisher: {}", e.getMessage()); } } /** * Initialize subscriber which broadcasts all received command events onto * the openHAB event bus. * * @param topic * to subscribe to. */ private void setupEventBusCommandSubscriber(String topic) { if (StringUtils.isBlank(topic)) { logger.trace("No topic defined for Event Bus Command Subscriber"); return; } try { topic = StringUtils.replace(topic, "${item}", "+"); logger.debug("Setting up Event Bus Command Subscriber for topic {}", topic); commandSubscriber = new MqttMessageSubscriber(brokerName + ":" + topic + ":command:default") { @Override public void processMessage(String topic, byte[] message) { String itemName = getItemNameFromTopic(getTopic(), topic); if (itemRegistry == null) { logger.error("Unable to lookup item {} for command; dropping", itemName); return; } Command command; try { Item item = itemRegistry.getItem(itemName); command = getCommand(new String(message), item.getAcceptedCommandTypes()); } catch (ItemNotFoundException e) { logger.debug("Unable to find item {} for command; dropping", itemName); return; } catch (Exception e) { logger.error("Error parsing command from message.", e); return; } eventPublisher.postCommand(itemName, command); } }; mqttService.registerMessageConsumer(brokerName, commandSubscriber); } catch (Exception e) { logger.error("Could not create event bus command subscriber: {}", e.getMessage()); } } /** * Initialize subscriber which broadcasts all received state events onto the * openHAB event bus. * * @param topic * to subscribe to. */ private void setupEventBusStateSubscriber(String topic) { if (StringUtils.isBlank(topic)) { logger.trace("No topic defined for Event Bus State Subscriber"); return; } try { topic = StringUtils.replace(topic, "${item}", "+"); logger.debug("Setting up Event Bus State Subscriber for topic {}", topic); stateSubscriber = new MqttMessageSubscriber(brokerName + ":" + topic + ":state:default") { @Override public void processMessage(String topic, byte[] message) { String itemName = getItemNameFromTopic(getTopic(), topic); if (itemRegistry == null) { logger.error("Unable to lookup item {} for update; dropping", itemName); return; } State state; try { Item item = itemRegistry.getItem(itemName); state = getState(new String(message), item.getAcceptedDataTypes()); } catch (ItemNotFoundException e) { logger.debug("Unable to find item {} for update; dropping", itemName); return; } catch (Exception e) { logger.error("Error parsing state from message.", e); return; } eventPublisher.postUpdate(itemName, state); } }; mqttService.registerMessageConsumer(brokerName, stateSubscriber); } catch (Exception e) { logger.error("Could not create event bus state subscriber: {}", e.getMessage()); } } /** * Initialize publisher which publishes all openHAB commands to the given * MQTT topic. * * @param topic * to subscribe to. */ private void setupEventBusCommandPublisher(String topic) { if (StringUtils.isBlank(topic)) { logger.trace("No topic defined for Event Bus Command Publisher"); return; } try { logger.debug("Setting up Event Bus Command Publisher for topic {}", topic); commandPublisher = new MqttMessagePublisher(brokerName + ":" + topic + ":command:*:default"); mqttService.registerMessageProducer(brokerName, commandPublisher); } catch (Exception e) { logger.error("Could not create event bus command publisher: {}", e.getMessage()); } } @Override public void updated(Dictionary<String, ?> properties) throws ConfigurationException { // load event bus pubish/subscribe configuration from configuration file if (properties == null || properties.isEmpty()) { logger.trace("No mqtt-eventbus properties configured."); return; } logger.debug("Initializing MQTT Event Bus Binding"); // stop existing publishers/subscribers deactivate(); brokerName = (String) properties.get("broker"); if (StringUtils.isEmpty(brokerName)) { logger.debug("No broker name configured for MQTT EventBusBinding"); return; } setupEventBusStatePublisher((String) properties.get("statePublishTopic")); setupEventBusStateSubscriber((String) properties.get("stateSubscribeTopic")); setupEventBusCommandPublisher((String) properties.get("commandPublishTopic")); setupEventBusCommandSubscriber((String) properties.get("commandSubscribeTopic")); logger.debug("MQTT Event Bus Binding initialization completed."); } }