/** * 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.lightwaverf.internal; import java.net.SocketException; import java.net.UnknownHostException; import java.util.Collection; import java.util.List; import java.util.Map; import org.apache.commons.lang.StringUtils; import org.openhab.binding.lightwaverf.LightwaveRfBindingProvider; import org.openhab.binding.lightwaverf.internal.command.LightwaveRFCommand; import org.openhab.binding.lightwaverf.internal.command.LightwaveRfCommandOk; import org.openhab.binding.lightwaverf.internal.command.LightwaveRfHeatInfoRequest; import org.openhab.binding.lightwaverf.internal.command.LightwaveRfRoomDeviceMessage; import org.openhab.binding.lightwaverf.internal.command.LightwaveRfRoomMessage; import org.openhab.binding.lightwaverf.internal.command.LightwaveRfSerialMessage; import org.openhab.binding.lightwaverf.internal.command.LightwaveRfVersionMessage; import org.openhab.binding.lightwaverf.internal.message.LightwaveRFMessageListener; import org.openhab.core.binding.AbstractBinding; import org.openhab.core.binding.BindingChangeListener; import org.openhab.core.binding.BindingProvider; import org.openhab.core.types.Command; import org.openhab.core.types.State; import org.openhab.core.types.Type; import org.osgi.framework.BundleContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Neil Renaud * @since 1.7.0 */ public class LightwaveRfBinding extends AbstractBinding<LightwaveRfBindingProvider> implements LightwaveRFMessageListener, BindingChangeListener { private static final Logger logger = LoggerFactory.getLogger(LightwaveRfBinding.class); private static int TIME_BETWEEN_SENT_MESSAGES_MS = 100; private static int TIMEOUT_FOR_OK_MESSAGES_MS = 500; // LightwaveRF WIFI hub port. private static int LIGHTWAVE_PORT_TO_SEND_TO = 9760; private static int LIGHTWAVE_PORTS_TO_RECEIVE_ON = 9761; // LightwaveRF WIFI hub IP Address or broadcast address private static String LIGHTWAVE_IP = "255.255.255.255"; private static boolean SEND_REGISTER_ON_STARTUP = true; private LightwaverfConvertor messageConvertor = new LightwaverfConvertor(); private LightwaveRfWifiLink wifiLink = null; private LightwaveRfHeatPoller heatPoller = null; public LightwaveRfBinding() { } /** * Called by the SCR to activate the component with its configuration read * from CAS * * @param bundleContext * BundleContext of the Bundle that defines this component * @param configuration * Configuration properties for this component obtained from the * ConfigAdmin service */ public void activate(final BundleContext bundleContext, final Map<String, Object> configuration) { String ipString = (String) configuration.get("ip"); if (StringUtils.isNotBlank(ipString)) { LIGHTWAVE_IP = ipString; } String recieverPortsString = (String) configuration.get("receiveport"); if (StringUtils.isNotBlank(recieverPortsString)) { LIGHTWAVE_PORTS_TO_RECEIVE_ON = Integer.parseInt(recieverPortsString); } String portTwoString = (String) configuration.get("sendport"); if (StringUtils.isNotBlank(portTwoString)) { LIGHTWAVE_PORT_TO_SEND_TO = Integer.parseInt(portTwoString); } String sendRegistrationMessageString = (String) configuration.get("registeronstartup"); if (StringUtils.isNotBlank(sendRegistrationMessageString)) { SEND_REGISTER_ON_STARTUP = Boolean.parseBoolean(sendRegistrationMessageString); } String sendDelayString = (String) configuration.get("senddelay"); if (StringUtils.isNotBlank(sendDelayString)) { TIME_BETWEEN_SENT_MESSAGES_MS = Integer.parseInt(sendDelayString); } String okTimeoutString = (String) configuration.get("okTimeout"); if (StringUtils.isNotBlank(okTimeoutString)) { TIMEOUT_FOR_OK_MESSAGES_MS = Integer.parseInt(okTimeoutString); } logger.info("LightwaveBinding: IP[{}]", LIGHTWAVE_IP); logger.info("LightwaveBinding: ReceivePort[{}]", LIGHTWAVE_PORTS_TO_RECEIVE_ON); logger.info("LightwaveBinding: Send Port[{}]", LIGHTWAVE_PORT_TO_SEND_TO); logger.info("LightwaveBinding: Register On Startup[{}]", SEND_REGISTER_ON_STARTUP); logger.info("LightwaveBinding: Send Delay [{}]", TIME_BETWEEN_SENT_MESSAGES_MS); logger.info("LightwaveBinding: Timeout for Ok Messages [{}]", TIMEOUT_FOR_OK_MESSAGES_MS); messageConvertor = new LightwaverfConvertor(); try { wifiLink = new LightwaveRfWifiLink(LIGHTWAVE_IP, LIGHTWAVE_PORT_TO_SEND_TO, LIGHTWAVE_PORTS_TO_RECEIVE_ON, messageConvertor, TIME_BETWEEN_SENT_MESSAGES_MS, TIMEOUT_FOR_OK_MESSAGES_MS); wifiLink.addListener(this); wifiLink.start(); if (SEND_REGISTER_ON_STARTUP) { wifiLink.sendLightwaveCommand(messageConvertor.getRegistrationCommand()); } // Now the sender is started and we have sent the registration // message // start the Heat Poller heatPoller = new LightwaveRfHeatPoller(wifiLink, messageConvertor); // Now register pollers if we have them. It might be that provider // hasn't // been setup yet and in that case they'll be registered by the // bindingChanged method for (LightwaveRfBindingProvider provider : providers) { Collection<String> itemNames = provider.getItemNames(); registerHeatingPollers(provider, itemNames); } } catch (UnknownHostException e) { logger.error("Error creating LightwaveRFSender", e); } catch (SocketException e) { logger.error("Error creating LightwaveRFSender/Receiver", e); } } /** * Used to active the binding when running as a test with the * wifilink set manually */ public void activateForTesting() { wifiLink.addListener(this); wifiLink.start(); // Now the sender is started and we have sent the registration // message // start the Heat Poller heatPoller = new LightwaveRfHeatPoller(wifiLink, messageConvertor); // Now register pollers if we have them. It might be that provider // hasn't // been setup yet and in that case they'll be registered by the // bindingChanged method for (LightwaveRfBindingProvider provider : providers) { Collection<String> itemNames = provider.getItemNames(); registerHeatingPollers(provider, itemNames); } } @Override public void bindingChanged(BindingProvider provider, String itemName) { super.bindingChanged(provider, itemName); if (provider instanceof LightwaveRfBindingProvider) { logger.info("LightwaveRf Binding changed for: {}", itemName); registerHeatingPoller((LightwaveRfBindingProvider) provider, itemName); } } private void registerHeatingPollers(LightwaveRfBindingProvider provider, Collection<String> itemNames) { for (String itemName : itemNames) { registerHeatingPoller(provider, itemName); } } private void registerHeatingPoller(LightwaveRfBindingProvider provider, String itemName) { int poll = provider.getPollInterval(itemName); if (poll > 0) { String roomId = provider.getRoomId(itemName); heatPoller.addRoomToPoll(itemName, roomId, poll); } else { heatPoller.removeRoomToPoll(itemName); } } protected void addBindingProvider(LightwaveRfBindingProvider bindingProvider) { super.addBindingProvider(bindingProvider); } protected void removeBindingProvider(LightwaveRfBindingProvider bindingProvider) { super.removeBindingProvider(bindingProvider); } /** * Called by the SCR when the configuration of a binding has been changed * through the ConfigAdmin service. * * @param configuration * Updated configuration properties */ public void modified(final Map<String, Object> configuration) { // update the internal configuration accordingly } /** * Called by the SCR to deactivate the component when either the * configuration is removed or mandatory references are no longer satisfied * or the component has simply been stopped. * * @param reason * Reason code for the deactivation:<br> * <ul> * <li>0 – Unspecified * <li>1 – The component was disabled * <li>2 – A reference became unsatisfied * <li>3 – A configuration was changed * <li>4 – A configuration was deleted * <li>5 – The component was disposed * <li>6 – The bundle was stopped * </ul> */ public void deactivate(final int reason) { // deallocate resources here that are no longer needed and // should be reset when activating this binding again // TODO unregister listeners heatPoller.stop(); wifiLink.stop(); heatPoller = null; wifiLink = null; messageConvertor = null; } /** * @{inheritDoc */ @Override protected void internalReceiveCommand(String itemName, Command command) { logger.debug("internalReceiveCommand({},{}) is called!", itemName, command); internalReceive(itemName, command); } /** * @{inheritDoc */ @Override protected void internalReceiveUpdate(String itemName, State newState) { logger.debug("internalReceiveUpdate({},{}) is called!", itemName, newState); internalReceive(itemName, newState); } private void internalReceive(String itemName, Type command) { LightwaveRfItemDirection direction = getDirection(itemName); if (direction == LightwaveRfItemDirection.IN_AND_OUT || direction == LightwaveRfItemDirection.OUT_ONLY) { String roomId = getRoomId(itemName); String deviceId = getDeviceId(itemName); LightwaveRfType deviceType = getType(itemName); LightwaveRFCommand lightwaverfMessageString = messageConvertor.convertToLightwaveRfMessage(roomId, deviceId, deviceType, command); wifiLink.sendLightwaveCommand(lightwaverfMessageString); } else { logger.debug("Not sending command[" + command + "] to item[" + itemName + "] as it is IN_ONLY"); } } private LightwaveRfItemDirection getDirection(String itemName) { for (LightwaveRfBindingProvider provider : providers) { LightwaveRfItemDirection direction = provider.getDirection(itemName); if (direction != null) { return direction; } } return null; } private String getRoomId(String itemName) { for (LightwaveRfBindingProvider provider : providers) { String roomId = provider.getRoomId(itemName); if (roomId != null) { return roomId; } } return null; } private String getDeviceId(String itemName) { for (LightwaveRfBindingProvider provider : providers) { String deviceId = provider.getDeviceId(itemName); if (deviceId != null) { return deviceId; } } return null; } private LightwaveRfType getType(String itemName) { for (LightwaveRfBindingProvider provider : providers) { LightwaveRfType type = provider.getTypeForItemName(itemName); if (type != null) { return type; } } return null; } private void publishUpdate(List<String> itemNames, LightwaveRFCommand message, LightwaveRfBindingProvider provider) { logger.debug("Publishing Update {} to {}", message, itemNames); boolean published = false; if (itemNames != null) { for (String itemName : itemNames) { LightwaveRfItemDirection direction = provider.getDirection(itemName); if (direction == LightwaveRfItemDirection.IN_AND_OUT || direction == LightwaveRfItemDirection.IN_ONLY) { LightwaveRfType deviceType = provider.getTypeForItemName(itemName); State state = message.getState(deviceType); if (state != null) { logger.info("Update from LightwaveRf ItemName[{}], State[{}]", itemName, state); published = true; eventPublisher.postUpdate(itemName, state); } else { logger.info("State was null for {} type {}, message {}", new Object[] { itemName, deviceType, message }); } } else { logger.debug("Not publishing message[{}] as Item[{}] is OUT_ONLY", message.getLightwaveRfCommandString(), itemName); } } } if (!published) { logger.warn("No item for incoming message[{}]", message.getLightwaveRfCommandString()); } } @Override public void roomDeviceMessageReceived(LightwaveRfRoomDeviceMessage message) { for (LightwaveRfBindingProvider provider : providers) { List<String> itemNames = provider.getBindingItemsForRoomDevice(message.getRoomId(), message.getDeviceId()); publishUpdate(itemNames, message, provider); } } @Override public void roomMessageReceived(LightwaveRfRoomMessage message) { for (LightwaveRfBindingProvider provider : providers) { List<String> itemNames = provider.getBindingItemsForRoom(message.getRoomId()); publishUpdate(itemNames, message, provider); } } @Override public void serialMessageReceived(LightwaveRfSerialMessage message) { for (LightwaveRfBindingProvider provider : providers) { List<String> itemNames = provider.getBindingItemsForSerial(message.getSerial()); publishUpdate(itemNames, message, provider); } } @Override public void okMessageReceived(LightwaveRfCommandOk message) { // Do nothing } @Override public void heatInfoMessageReceived(LightwaveRfHeatInfoRequest command) { // Do nothing } @Override public void versionMessageReceived(LightwaveRfVersionMessage message) { for (LightwaveRfBindingProvider provider : providers) { List<String> itemNames = provider.getBindingItemsForType(LightwaveRfType.VERSION); publishUpdate(itemNames, message, provider); } } /** * Visible for testing only to allow us to add a mock Convertor * * @param mockLightwaveRfConvertor */ void setLightwaveRfConvertor(LightwaverfConvertor mockLightwaveRfConvertor) { this.messageConvertor = mockLightwaveRfConvertor; } /** * Visible for testing only to allow us to add a mock Wifi Link * * @param mockWifiLink */ void setWifiLink(LightwaveRfWifiLink mockWifiLink) { this.wifiLink = mockWifiLink; } }