Java tutorial
/** * Copyright (c) 2010-2015, openHAB.org and others. * * 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 static org.quartz.JobBuilder.newJob; import static org.quartz.SimpleScheduleBuilder.simpleSchedule; import static org.quartz.TriggerBuilder.newTrigger; import static org.quartz.impl.matchers.GroupMatcher.jobGroupEquals; import java.util.ArrayList; import java.util.Dictionary; import java.util.Enumeration; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.lang.IllegalClassException; import org.openhab.binding.plugwise.PlugwiseBindingProvider; import org.openhab.binding.plugwise.PlugwiseCommandType; import org.openhab.binding.plugwise.internal.PlugwiseGenericBindingProvider.PlugwiseBindingConfigElement; import org.openhab.core.binding.AbstractActiveBinding; import org.openhab.core.types.Command; import org.openhab.core.types.State; import org.openhab.core.types.Type; import org.openhab.core.types.TypeParser; import org.openhab.model.item.binding.BindingConfigParseException; import org.osgi.service.cm.ConfigurationException; import org.osgi.service.cm.ManagedService; import org.quartz.JobDataMap; import org.quartz.JobDetail; import org.quartz.JobKey; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.Trigger; import org.quartz.impl.StdSchedulerFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Main binding class * * @author Karel Goderis * @since 1.1.0 */ public class PlugwiseBinding extends AbstractActiveBinding<PlugwiseBindingProvider> implements ManagedService { private static final Logger logger = LoggerFactory.getLogger(PlugwiseBinding.class); private static final Pattern EXTRACT_PLUGWISE_CONFIG_PATTERN = Pattern.compile("^(.*?)\\.(mac|port|interval)$"); /** the refresh interval which is used to check for changes in the binding configurations */ private static long refreshInterval = 5000; private Stick stick; @SuppressWarnings("rawtypes") @Override public void updated(Dictionary config) throws ConfigurationException { if (config != null) { // First of all make sure the Stick gets set up Enumeration keys = config.keys(); while (keys.hasMoreElements()) { String key = (String) keys.nextElement(); // the config-key enumeration contains additional keys that we // don't want to process here ... if ("service.pid".equals(key)) { continue; } Matcher matcher = EXTRACT_PLUGWISE_CONFIG_PATTERN.matcher(key); if (!matcher.matches()) { logger.error("given plugwise-config-key '" + key + "' does not follow the expected pattern '<PlugwiseId>.<mac|port|interval>'"); continue; } matcher.reset(); matcher.find(); String plugwiseID = matcher.group(1); if (plugwiseID.equals("stick")) { if (stick == null) { String configKey = matcher.group(2); String value = (String) config.get(key); if ("port".equals(configKey)) { stick = new Stick(value, this); logger.info("Plugwise added Stick connected to serial port {}", value); } else if ("interval".equals(configKey)) { // do nothing for now. we will set in the second run } else if ("retries".equals(configKey)) { // do nothing for now. we will set in the second run } else { throw new ConfigurationException(configKey, "the given configKey '" + configKey + "' is unknown"); } } } } if (stick != null) { // re-run through the configuration and setup the remaining devices keys = config.keys(); while (keys.hasMoreElements()) { String key = (String) keys.nextElement(); // the config-key enumeration contains additional keys that we // don't want to process here ... if ("service.pid".equals(key)) { continue; } Matcher matcher = EXTRACT_PLUGWISE_CONFIG_PATTERN.matcher(key); if (!matcher.matches()) { logger.error("given plugwise-config-key '" + key + "' does not follow the expected pattern '<PlugwiseId>.<mac|port>'"); continue; } matcher.reset(); matcher.find(); String plugwiseID = matcher.group(1); if (plugwiseID.equals("stick")) { String configKey = matcher.group(2); String value = (String) config.get(key); if ("interval".equals(configKey)) { stick.setInterval(Integer.valueOf(value)); logger.info("Setting the interval to send ZigBee PDUs to {} ms", value); } else if ("retries".equals(configKey)) { stick.setRetries(Integer.valueOf(value)); logger.info("Setting the maximum number of attempts to send a message to ", value); } else if ("port".equals(configKey)) { //ignore } else { throw new ConfigurationException(configKey, "the given configKey '" + configKey + "' is unknown"); } } PlugwiseDevice device = stick.getDeviceByName(plugwiseID); if (device == null && !plugwiseID.equals("stick")) { String configKey = matcher.group(2); String value = (String) config.get(key); String MAC = null; if ("mac".equals(configKey)) { MAC = value; } else { throw new ConfigurationException(configKey, "the given configKey '" + configKey + "' is unknown"); } if (!MAC.equals("")) { if (plugwiseID.equals("circleplus")) { if (stick.getDeviceByMAC(MAC) == null) { device = new CirclePlus(MAC, stick); logger.info("Plugwise added Circle+ with MAC address: {}", MAC); } } else { if (stick.getDeviceByMAC(MAC) == null) { device = new Circle(MAC, stick, plugwiseID); logger.info("Plugwise added Circle with MAC address: {}", MAC); } } stick.plugwiseDeviceCache.add(device); } } } setProperlyConfigured(true); } else { logger.error("Plugwise needs at least one Stick in order to operate"); } } } public void activate() { // Nothing to do here. We start the binding when the first item bindigconfig is processed } public void deactivate() { if (stick != null) { //unschedule all the quartz jobs Scheduler sched = null; try { sched = StdSchedulerFactory.getDefaultScheduler(); } catch (SchedulerException e) { logger.error("An exception occurred while getting a reference to the Quarz Scheduler"); } for (PlugwiseBindingProvider provider : providers) { try { for (JobKey jobKey : sched.getJobKeys(jobGroupEquals("Plugwise-" + provider.toString()))) { sched.deleteJob(jobKey); } } catch (SchedulerException e) { logger.error("An exception occurred while deleting the Plugwise Quartz jobs ({})", e.getMessage()); } } stick.close(); } } @Override protected void internalReceiveCommand(String itemName, Command command) { PlugwiseBindingProvider provider = findFirstMatchingBindingProvider(itemName); String commandAsString = command.toString(); if (command != null) { List<Command> commands = new ArrayList<Command>(); // check if the command is valid for this item by checking if a pw ID exists String checkID = provider.getPlugwiseID(itemName, command); if (checkID != null) { commands.add(command); } else { // ooops - command is not defined, but maybe we have something of the same Type (e.g Decimal, String types) //commands = provider.getCommandsByType(itemName, command.getClass()); commands = provider.getAllCommands(itemName); } for (Command someCommand : commands) { String plugwiseID = provider.getPlugwiseID(itemName, someCommand); PlugwiseCommandType plugwiseCommandType = provider.getPlugwiseCommandType(itemName, someCommand); if (plugwiseID != null) { if (plugwiseCommandType != null) { @SuppressWarnings("unused") boolean result = executeCommand(plugwiseID, plugwiseCommandType, commandAsString); // Each command is responsible to make sure that a result value for the action is polled from the device // which then will be used to do a postUpdate // if new commands would be added later on that do not have this possibility, then a kind of // auto-update has to be performed here below } else { logger.error("wrong command type for binding [Item={}, command={}]", itemName, commandAsString); } } else { logger.error("{} is an unrecognised command for Item {}", commandAsString, itemName); } } } } private boolean executeCommand(String plugwiseID, PlugwiseCommandType plugwiseCommandType, String commandAsString) { boolean result = false; if (plugwiseID != null) { PlugwiseDevice plug = stick.getDeviceByMAC(plugwiseID); if (plug != null) { switch (plugwiseCommandType) { case CURRENTSTATE: if (plug instanceof Circle || plug instanceof CirclePlus) { result = ((Circle) plug).setPowerState(commandAsString); ((Circle) plug).updateInformation(); } default: break; } ; } else { logger.error("Plugwise device is not defined for device with ID {}", plugwiseID); } } return result; } /** * Method to post updates to the OH runtime. * * * @param MAC of the Plugwise device concerned * @param ctype is the Plugwise Command type * @param value is the value (to be converted) to post */ public void postUpdate(String MAC, PlugwiseCommandType ctype, Object value) { if (MAC != null && ctype != null && value != null) { for (PlugwiseBindingProvider provider : providers) { Set<String> qualifiedItems = provider.getItemNames(MAC, ctype); // Make sure we also capture those devices that were pre-defined with a friendly name in a .cfg or alike Set<String> qualifiedItemsFriendly = provider.getItemNames(stick.getDevice(MAC).getFriendlyName(), ctype); qualifiedItems.addAll(qualifiedItemsFriendly); Type type = null; try { type = createStateForType(ctype, value.toString()); } catch (BindingConfigParseException e) { logger.error("Error parsing a value {} to a state variable of type {}", value.toString(), ctype.getTypeClass().toString()); } for (String anItem : qualifiedItems) { if (type instanceof State) { eventPublisher.postUpdate(anItem, (State) type); } else { throw new IllegalClassException("Cannot process update of type " + type.toString()); } } } } } @SuppressWarnings("unchecked") private Type createStateForType(PlugwiseCommandType ctype, String value) throws BindingConfigParseException { Class<? extends Type> typeClass = ctype.getTypeClass(); List<Class<? extends State>> stateTypeList = new ArrayList<Class<? extends State>>(); stateTypeList.add((Class<? extends State>) typeClass); State state = TypeParser.parseState(stateTypeList, value); return state; } /** * Find the first matching {@link PlugwiseBindingProvider} * according to <code>itemName</code> * * @param itemName * * @return the matching binding provider or <code>null</code> if no binding * provider could be found */ protected PlugwiseBindingProvider findFirstMatchingBindingProvider(String itemName) { PlugwiseBindingProvider firstMatchingProvider = null; for (PlugwiseBindingProvider provider : providers) { List<String> plugwiseIDs = provider.getPlugwiseID(itemName); if (plugwiseIDs != null && plugwiseIDs.size() > 0) { firstMatchingProvider = provider; break; } } return firstMatchingProvider; } @Override protected void execute() { if (isProperlyConfigured()) { Scheduler sched = null; try { sched = StdSchedulerFactory.getDefaultScheduler(); } catch (SchedulerException e) { logger.error("An exception occurred while getting a reference to the Quartz Scheduler"); } for (PlugwiseBindingProvider provider : providers) { List<PlugwiseBindingConfigElement> compiledList = ((PlugwiseBindingProvider) provider) .getIntervalList(); Iterator<PlugwiseBindingConfigElement> pbcIterator = compiledList.iterator(); while (pbcIterator.hasNext()) { PlugwiseBindingConfigElement anElement = pbcIterator.next(); PlugwiseCommandType type = anElement.getCommandType(); // check if the device already exists (via cfg definition of Role Call) if (stick.getDevice(anElement.getId()) == null) { logger.debug("The Plugwise device with id {} is not yet defined", anElement.getId()); // check if the config string really contains a MAC address Pattern MAC_PATTERN = Pattern.compile("(\\w{16})"); Matcher matcher = MAC_PATTERN.matcher(anElement.getId()); if (matcher.matches()) { CirclePlus cp = (CirclePlus) stick.getDeviceByName("circleplus"); if (cp != null) { if (!cp.getMAC().equals(anElement.getId())) { //a circleplus has been added/detected and it is not what is in the binding config PlugwiseDevice device = new Circle(anElement.getId(), stick, anElement.getId()); stick.plugwiseDeviceCache.add(device); logger.info("Plugwise added Circle with MAC address: {}", anElement.getId()); } } else { logger.warn( "Plugwise can not guess the device that should be added. Consider defining it in the openHAB configuration file"); } } else { logger.warn( "Plugwise can not add a valid device without a proper MAC address. {} can not be used", anElement.getId()); } } if (stick.getDevice(anElement.getId()) != null) { boolean jobExists = false; // enumerate each job group try { for (String group : sched.getJobGroupNames()) { // enumerate each job in group for (JobKey jobKey : sched.getJobKeys(jobGroupEquals(group))) { if (jobKey.getName() .equals(anElement.getId() + "-" + type.getJobClass().toString())) { jobExists = true; break; } } } } catch (SchedulerException e1) { logger.error("An exception occurred while quering the Quartz Scheduler ({})", e1.getMessage()); } if (!jobExists) { // set up the Quartz jobs JobDataMap map = new JobDataMap(); map.put("Stick", stick); map.put("MAC", stick.getDevice(anElement.getId()).MAC); JobDetail job = newJob(type.getJobClass()) .withIdentity(anElement.getId() + "-" + type.getJobClass().toString(), "Plugwise-" + provider.toString()) .usingJobData(map).build(); Trigger trigger = newTrigger() .withIdentity(anElement.getId() + "-" + type.getJobClass().toString(), "Plugwise-" + provider.toString()) .startNow().withSchedule(simpleSchedule().repeatForever() .withIntervalInSeconds(anElement.getInterval())) .build(); try { sched.scheduleJob(job, trigger); } catch (SchedulerException e) { logger.error("An exception occurred while scheduling a Quartz Job"); } } } else { logger.error("Error scheduling a Quartz Job for a non-defined Plugwise device"); } } } } } @Override protected long getRefreshInterval() { return refreshInterval; } @Override protected String getName() { return "Plugwise Refresh Service"; } }