/**
* 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.plugwise.internal;
import org.joda.time.DateTime;
import org.openhab.binding.plugwise.PlugwiseCommandType;
import org.openhab.binding.plugwise.protocol.AcknowledgeMessage;
import org.openhab.binding.plugwise.protocol.ClockSetRequestMessage;
import org.openhab.binding.plugwise.protocol.Message;
import org.openhab.binding.plugwise.protocol.RealTimeClockGetRequestMessage;
import org.openhab.binding.plugwise.protocol.RealTimeClockGetResponseMessage;
import org.openhab.binding.plugwise.protocol.RoleCallRequestMessage;
import org.openhab.binding.plugwise.protocol.RoleCallResponseMessage;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A class that represents a Plugwise Circle+ device
*
* Circle+ are special Circles. Typically there is one Circle+ in a Plugwise network, and it serves as a master
* controller in the network, providing Clock data to the other Circles, relay information to the Stick and so forth
*
* Every 24h the Clock of the Circle+ is set identical to the hosts' Clock
*
* The Circle+ also does "RoleCall"s, e.g. polling the Plugwise network in order to make an inventory of all the
* availble Circles
*
* @author Karel Goderis
* @since 1.1.0
*/
public class CirclePlus extends Circle {
private static final Logger logger = LoggerFactory.getLogger(CirclePlus.class);
public static final String CIRCLE_PLUS_JOB_DATA_KEY = "CirclePlus";
protected DateTime realtimeClock;
public CirclePlus(String mac, Stick stick, String friendly) {
super(mac, stick, friendly);
type = DeviceType.CirclePlus;
}
public boolean setClock() {
return setClock(DateTime.now());
}
public boolean setClock(DateTime stamp) {
ClockSetRequestMessage message = new ClockSetRequestMessage(MAC, stamp);
stick.sendMessage(message);
return true;
}
/**
* Role calling is basically asking the Circle+ to return all the devices known to it. Up to 64 devices
* are supported in a PW network, and role calling is done by sequentially sendng RoleCallMessages for all
* possible IDs in the network (ID = number from 1 to 63)
*
* @param id of the device to rolecall
*/
public void roleCall(int id) {
if (id >= 0 && id < 64) {
RoleCallRequestMessage request = new RoleCallRequestMessage(MAC, id);
stick.sendMessage(request);
}
}
public DateTime getRealTimeClock() {
if (realtimeClock != null) {
return realtimeClock;
} else {
updateRealTimeClock();
return null;
}
}
public void updateRealTimeClock() {
RealTimeClockGetRequestMessage message = new RealTimeClockGetRequestMessage(MAC);
stick.sendMessage(message);
}
@Override
public boolean processMessage(Message message) {
if (message != null) {
switch (message.getType()) {
case DEVICE_ROLECALL_RESPONSE:
if (((RoleCallResponseMessage) message).getNodeID() < 63
&& !((RoleCallResponseMessage) message).getNodeMAC().equals("FFFFFFFFFFFFFFFF")) {
// add e new node
PlugwiseDevice device = stick.getDeviceByMAC(((RoleCallResponseMessage) message).getNodeMAC());
if (device == null) {
// currently it is always assumed the device is a Circle, it would be better to
// detect the actual device type by sending an InformationRequestMessage to the MAC
// and use the device type in the InformationResponseMessage for dynamically adding
// devices
device = new Circle(((RoleCallResponseMessage) message).getNodeMAC(), stick,
((RoleCallResponseMessage) message).getNodeMAC());
stick.addDevice(device);
logger.debug("Added a Circle with MAC {} to the cache", device.getMAC());
}
if (device instanceof Circle) {
((Circle) device).updateInformation();
((Circle) device).calibrate();
}
// check if there is any other on the network
roleCall(((RoleCallResponseMessage) message).getNodeID() + 1);
}
return true;
case REALTIMECLOCK_GET_RESPONSE:
realtimeClock = ((RealTimeClockGetResponseMessage) message).getTime();
postUpdate(MAC, PlugwiseCommandType.REALTIMECLOCK, realtimeClock);
return true;
case ACKNOWLEDGEMENT:
if (((AcknowledgeMessage) message).isExtended()) {
switch (((AcknowledgeMessage) message).getExtensionCode()) {
case CLOCKSET:
logger.debug("Circle+ Clock is set");
break;
default:
return stick.processMessage(message);
}
}
default:
return super.processMessage(message);
}
} else {
return false;
}
}
public static class SetClockJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
// get the reference to the Stick
JobDataMap dataMap = context.getJobDetail().getJobDataMap();
CirclePlus circlePlus = (CirclePlus) dataMap.get(CIRCLE_PLUS_JOB_DATA_KEY);
circlePlus.setClock();
}
}
}