/**
* 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.ucprelayboard.internal;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.openhab.binding.ucprelayboard.UCPRelayBoardBindingProvider;
import org.openhab.core.binding.AbstractActiveBinding;
import org.openhab.core.items.Item;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.types.Command;
import org.openhab.core.types.State;
import org.osgi.framework.BundleContext;
/**
* Binding for relay boards available from http://www.ucprojects.eu/
*
* @author Robert Michalak
* @since 1.8.0
*/
public class UCPRelayBoardBinding extends AbstractActiveBinding<UCPRelayBoardBindingProvider> {
private Map<String, SerialDevice> serialDevices = new HashMap<String, SerialDevice>();
private long refreshInterval = 60000;
protected void addBindingProvider(UCPRelayBoardBindingProvider bindingProvider) {
super.addBindingProvider(bindingProvider);
}
protected void removeBindingProvider(UCPRelayBoardBindingProvider bindingProvider) {
super.removeBindingProvider(bindingProvider);
}
/**
* {@inheritDoc}
*/
@Override
public void internalReceiveCommand(String itemName, Command command) {
UCPRelayConfig config = getRelayConfigForItem(itemName);
if (config != null) {
SerialDevice serialDevice = serialDevices.get(config.getBoardName());
if (command instanceof OnOffType) {
byte[] relayBoardCommand = prepareRelayCommand(config, ((OnOffType) command));
serialDevice.writeBytes(relayBoardCommand);
}
}
}
private UCPRelayConfig getRelayConfigForItem(String itemName) {
for (UCPRelayBoardBindingProvider provider : providers) {
if (provider.providesBindingFor(itemName)) {
return provider.getRelayConfigForItem(itemName);
}
}
return null;
}
private byte[] prepareRelayCommand(UCPRelayConfig config, OnOffType onOffType) {
switch (config.translateCommand(onOffType)) {
case OFF:
return UCPRelayBoardMessages.OFF[config.getRelayNumber()];
case ON:
return UCPRelayBoardMessages.ON[config.getRelayNumber()];
}
return null;
}
public void activate(final BundleContext bundleContext, final Map<String, Object> properties)
throws InitializationException {
if (properties == null) {
return;
}
Map<String, SerialDeviceConfig> configs = prepareConfiguration(properties);
for (SerialDeviceConfig config : configs.values()) {
prepareSerialDevice(config);
}
if (serialDevices.size() > 0) {
setProperlyConfigured(true);
}
}
public void modified(final Map<String, Object> properties) throws InitializationException {
if (properties == null) {
return;
}
Map<String, SerialDeviceConfig> configs = prepareConfiguration(properties);
// create new and modify existing ones
for (Map.Entry<String, SerialDeviceConfig> config : configs.entrySet()) {
if (serialDevices.containsKey(config.getKey())) {
SerialDevice device = serialDevices.get(config.getKey());
if (!config.equals(device.getConfig())) {
device.close();
prepareSerialDevice(config.getValue());
}
} else {
prepareSerialDevice(config.getValue());
}
}
// close removed ones
for (String name : serialDevices.keySet()) {
if (!configs.containsKey(name)) {
SerialDevice device = serialDevices.get(name);
device.close();
serialDevices.remove(name);
}
}
if (serialDevices.size() > 0) {
setProperlyConfigured(true);
} else {
setProperlyConfigured(false);
}
}
private void prepareSerialDevice(SerialDeviceConfig config) throws InitializationException {
SerialDevice serialDevice = new SerialDevice(config);
serialDevice.initialize();
serialDevices.put(config.getName(), serialDevice);
}
private Map<String, SerialDeviceConfig> prepareConfiguration(final Map<String, Object> properties) {
Map<String, SerialDeviceConfig> configs = new HashMap<String, SerialDeviceConfig>();
for (String key : properties.keySet()) {
Object property = properties.get(key);
if (!(property instanceof String)) {
continue;
}
String value = StringUtils.trimToNull((String) property);
if ("refresh".equals(key)) {
try {
refreshInterval = Long.parseLong(value);
} catch (NumberFormatException ignored) {
}
continue;
}
String[] split = StringUtils.split(key, ".");
if (split.length != 3 || value == null || !"board".equals(split[0])) {
continue;
}
SerialDeviceConfig config = configs.get(split[1]);
if (config == null) {
config = new SerialDeviceConfig();
config.setName(split[1]);
configs.put(split[1], config);
}
if ("port".equals(split[2])) {
config.setPort(value);
} else if ("baud".equals(split[2])) {
config.setBaud(Integer.valueOf(value));
}
}
return configs;
}
public void deactivate(final int reason) {
for (SerialDevice serialDevice : serialDevices.values()) {
serialDevice.close();
}
serialDevices.clear();
}
private void retrieveStateFromRelayBoard(SerialDevice serialDevice) {
// consume any bytes left in a stream
serialDevice.readBytes(new byte[100]);
serialDevice.writeBytes(UCPRelayBoardMessages.GET_STATE);
try {
Thread.sleep(100);
} catch (InterruptedException ignored) {
}
readAndDecodeResponse(serialDevice);
}
private void readAndDecodeResponse(SerialDevice serialDevice) {
byte response[] = serialDevice.readBytes(new byte[5]);
if (validateResponse(response)) {
for (int relay = 0; relay < 8; relay++) {
Item item = getItemForRelay(serialDevice, relay);
if (item != null) {
UCPRelayConfig config = getRelayConfigForItem(item.getName());
State state = getRelayState(response, relay, config);
eventPublisher.postUpdate(item.getName(), state);
}
}
}
}
private boolean validateResponse(byte[] response) {
if (response[0] != 85) {
return false;
}
if (!checkCRC(response)) {
return false;
}
return true;
}
private boolean checkCRC(byte[] response) {
final byte[] tab = Arrays.copyOfRange(response, 1, 4);
final byte crc = DowCRC.compute(tab);
if (crc != response[4]) {
return false;
} else {
return true;
}
}
private OnOffType getRelayState(byte[] response, int relay, UCPRelayConfig config) {
OnOffType state;
if ((response[3] & (1 << relay)) > 0) {
state = OnOffType.ON;
} else {
state = OnOffType.OFF;
}
return config.translateCommand(state);
}
private Item getItemForRelay(SerialDevice serialDevice, int relay) {
for (UCPRelayBoardBindingProvider provider : providers) {
Item item = provider.getItemForRelayConfig(new UCPRelayConfig(serialDevice.getConfig().getName(), relay));
if (item != null) {
return item;
}
}
return null;
}
@Override
protected void execute() {
for (SerialDevice serialDevice : serialDevices.values()) {
retrieveStateFromRelayBoard(serialDevice);
}
}
@Override
protected long getRefreshInterval() {
return refreshInterval;
}
@Override
protected String getName() {
return "UCProjects.eu Relay Board Binding Thread";
}
}