/**
* 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.lcn.mappingtarget;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.openhab.binding.lcn.common.LcnAddr;
import org.openhab.binding.lcn.common.LcnAddrMod;
import org.openhab.binding.lcn.common.LcnDefs;
import org.openhab.binding.lcn.common.PckGenerator;
import org.openhab.binding.lcn.connection.Connection;
import org.openhab.binding.lcn.connection.ModInfo;
import org.openhab.binding.lcn.input.ModStatusBinSensors;
import org.openhab.binding.lcn.input.ModStatusKeyLocks;
import org.openhab.binding.lcn.input.ModStatusLedsAndLogicOps;
import org.openhab.binding.lcn.input.ModStatusOutput;
import org.openhab.binding.lcn.input.ModStatusRelays;
import org.openhab.binding.lcn.input.ModStatusVar;
import org.openhab.core.events.EventPublisher;
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;
/**
* Locks keys of a table.
* Can also visualize single key-states.
*
* @author Tobias J�ttner
*/
public class LockKeys extends TargetWithLcnAddr {
/** Pattern to parse key-lock commands. */
private static final Pattern PATTERN_LOCK_KEYS = Pattern.compile("(?<table>[ABCD])\\.(?<states>[10T-]{8})",
Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
/** 0(A)..3(D) */
private final int tableId;
/** Target key-lock states. */
private final LcnDefs.KeyLockStateModifier[] states;
/**
* Constructor.
*
* @param addr the target LCN address
* @param tableId 0..3
* @param states the 8 key-lock modifiers
*/
LockKeys(LcnAddr addr, int tableId, LcnDefs.KeyLockStateModifier[] states) {
super(addr);
this.tableId = tableId;
this.states = states;
}
/**
* Tries to parse the given input text.
*
* @param input the text to parse
* @return the parsed {@link LockKeys} or null
*/
static Target tryParseTarget(String input) {
CmdAndAddressRet header = CmdAndAddressRet.parse(input, true);
if (header != null) {
Matcher matcher;
switch (header.getCmd().toUpperCase()) {
case "LOCK":
if ((matcher = PATTERN_LOCK_KEYS.matcher(header.getRestInput())).matches()) {
LcnDefs.KeyLockStateModifier[] states = new LcnDefs.KeyLockStateModifier[8];
int tableId;
switch (matcher.group("table").toUpperCase()) {
case "A":
tableId = 0;
break;
case "B":
tableId = 1;
break;
case "C":
tableId = 2;
break;
case "D":
tableId = 3;
break;
default:
throw new Error();
}
for (int i = 0; i < 8; ++i) {
switch (matcher.group("states").toUpperCase().charAt(i)) {
case '1':
states[i] = LcnDefs.KeyLockStateModifier.ON;
break;
case '0':
states[i] = LcnDefs.KeyLockStateModifier.OFF;
break;
case 'T':
states[i] = LcnDefs.KeyLockStateModifier.TOGGLE;
break;
case '-':
states[i] = LcnDefs.KeyLockStateModifier.NOCHANGE;
break;
default:
throw new Error();
}
}
return new LockKeys(header.getAddr(), tableId, states);
}
break;
}
}
return null;
}
/** {@inheritDoc} */
@Override
public void send(Connection conn, Item item, Command cmd) {
conn.queue(this.addr, !this.addr.isGroup(), PckGenerator.lockKeys(this.tableId, this.states));
// Force status update (status is polled and should be updated now)
if (!this.addr.isGroup()) {
ModInfo info = conn.getModInfo((LcnAddrMod) this.addr);
if (info != null) {
info.requestStatusLockedKeys.nextRequestIn(ModInfo.STATUS_REQUEST_DELAY_AFTER_COMMAND_MSEC,
System.nanoTime());
}
}
}
/** {@inheritDoc} */
@Override
public void register(Connection conn) {
if (!this.addr.isGroup()) {
ModInfo info = conn.updateModuleData((LcnAddrMod) this.addr);
if (!info.requestStatusLockedKeys.isActive()) {
info.requestStatusLockedKeys.nextRequestIn(0, System.nanoTime());
}
}
}
/** {@inheritDoc} */
@Override
public boolean visualizationHandleOutputStatus(ModStatusOutput pchkInput, Command cmd, Item item,
EventPublisher eventPublisher) {
return false;
}
/** {@inheritDoc} */
@Override
public boolean visualizationHandleRelaysStatus(ModStatusRelays pchkInput, Command cmd, Item item,
EventPublisher eventPublisher) {
return false;
}
/** {@inheritDoc} */
@Override
public boolean visualizationBinSensorsStatus(ModStatusBinSensors pchkInput, Command cmd, Item item,
EventPublisher eventPublisher) {
return false;
}
/** {@inheritDoc} */
@Override
public boolean visualizationVarStatus(ModStatusVar pchkInput, Command cmd, Item item,
EventPublisher eventPublisher) {
return false;
}
/** {@inheritDoc} */
@Override
public boolean visualizationLedsAndLogicOpsStatus(ModStatusLedsAndLogicOps pchkInput, Command cmd, Item item,
EventPublisher eventPublisher) {
return false;
}
/**
* Visualization of {@link OnOffType}.
* {@inheritDoc}
*/
@Override
public boolean visualizationKeyLocksStatus(ModStatusKeyLocks pchkInput, Command cmd, Item item,
EventPublisher eventPublisher) {
// We are actually not meant to visualize anything.
// But (just in case) someone is really lazy in doing the item-definitions, we try to be helpful by implementing
// some ON/OFF logic.
if (pchkInput.getLogicalSourceAddr().equals(this.addr)
&& item.getAcceptedDataTypes().contains(OnOffType.class)) {
for (int i = 0; i < 8; ++i) {
if (this.states[i] == LcnDefs.KeyLockStateModifier.ON
|| this.states[i] == LcnDefs.KeyLockStateModifier.OFF) {
State reportedState = pchkInput.getState(this.tableId, i) ? OnOffType.ON : OnOffType.OFF;
// Only update if the state we are bound to is equal to the reported one.
if ((this.states[i] == LcnDefs.KeyLockStateModifier.ON ? OnOffType.ON
: OnOffType.OFF) == reportedState) {
eventPublisher.postUpdate(item.getName(), reportedState);
return true;
}
break;
}
}
}
return false;
}
}