/**
  * Handle incoming message for this device by forwarding it to all features that this device
  * supports
  *
  * @param fromPort port from which the message come in
  * @param msg the incoming message
  */
 public void handleMessage(String fromPort, Msg msg) {
   synchronized (m_lastMsgReceived) {
     m_lastMsgReceived = System.currentTimeMillis();
   }
   synchronized (m_features) {
     // first update all features that are
     // not status features
     for (DeviceFeature f : m_features.values()) {
       if (!f.isStatusFeature()) {
         if (f.handleMessage(msg, fromPort)) {
           // handled a reply to a query,
           // mark it as processed
           logger.trace("handled reply of direct: {}", f);
           setFeatureQueried(null);
           break;
         }
       }
     }
     // then update all the status features,
     // e.g. when the device was last updated
     for (DeviceFeature f : m_features.values()) {
       if (f.isStatusFeature()) {
         f.handleMessage(msg, fromPort);
       }
     }
   }
 }
  /**
   * Execute poll on this device: create an array of messages, add them to the request queue, and
   * schedule the queue for processing.
   *
   * @param delay scheduling delay (in milliseconds)
   */
  public void doPoll(long delay) {
    long now = System.currentTimeMillis();
    ArrayList<QEntry> l = new ArrayList<QEntry>();
    synchronized (m_features) {
      int spacing = 0;
      for (DeviceFeature i : m_features.values()) {
        if (i.hasListeners()) {
          Msg m = i.makePollMsg();
          if (m != null) l.add(new QEntry(i, m, now + delay + spacing));
        }
        spacing += TIME_BETWEEN_POLL_MESSAGES;
      }
    }
    if (l.isEmpty()) return;
    synchronized (m_requestQueue) {
      for (QEntry e : l) {
        m_requestQueue.add(e);
      }
    }
    RequestQueueManager.s_instance().addQueue(this, now + delay);

    if (!l.isEmpty()) {
      synchronized (m_lastTimePolled) {
        m_lastTimePolled = now;
      }
    }
  }
 public boolean hasAnyListeners() {
   synchronized (m_features) {
     for (DeviceFeature f : m_features.values()) {
       if (f.hasListeners()) return true;
     }
   }
   return false;
 }
 /**
  * Invoked to process an openHAB command
  *
  * @param driver The driver to use
  * @param c The item configuration
  * @param command The actual command to execute
  */
 public void processCommand(Driver driver, InsteonPLMBindingConfig c, Command command) {
   logger.debug("processing command {} features: {}", command, m_features.size());
   synchronized (m_features) {
     for (DeviceFeature i : m_features.values()) {
       if (i.isReferencedByItem(c.getItemName())) {
         i.handleCommand(c, command);
       }
     }
   }
 }
 /**
  * Removes feature listener from this device
  *
  * @param aItemName name of the feature listener to remove
  * @return true if a feature listener was successfully removed
  */
 public boolean removeFeatureListener(String aItemName) {
   boolean removedListener = false;
   synchronized (m_features) {
     for (Iterator<Entry<String, DeviceFeature>> it = m_features.entrySet().iterator();
         it.hasNext(); ) {
       DeviceFeature f = it.next().getValue();
       if (f.removeListener(aItemName)) {
         removedListener = true;
       }
     }
   }
   return removedListener;
 }
 private void connectFeatures(String gn, DeviceFeature fg, ArrayList<String> features) {
   for (String fs : features) {
     DeviceFeature f = m_features.get(fs);
     if (f == null) {
       logger.error("feature group {} references unknown feature {}", gn, fs);
     } else {
       logger.debug("{} connected feature: {}", gn, f);
       fg.addConnectedFeature(f);
     }
   }
 }
 private void instantiateFeatures(DeviceType dt) {
   for (Entry<String, String> fe : dt.getFeatures().entrySet()) {
     DeviceFeature f = DeviceFeature.s_makeDeviceFeature(fe.getValue());
     if (f == null) {
       logger.error("device type {} references unknown feature: {}", dt, fe.getValue());
     } else {
       addFeature(fe.getKey(), f);
     }
   }
   for (Entry<String, FeatureGroup> fe : dt.getFeatureGroups().entrySet()) {
     FeatureGroup fg = fe.getValue();
     DeviceFeature f = DeviceFeature.s_makeDeviceFeature(fg.getType());
     if (f == null) {
       logger.error("device type {} references unknown feature group: {}", dt, fg.getType());
     } else {
       addFeature(fe.getKey(), f);
     }
     connectFeatures(fe.getKey(), f, fg.getFeatures());
   }
 }
 private void addFeature(String name, DeviceFeature f) {
   f.setDevice(this);
   synchronized (m_features) {
     m_features.put(name, f);
   }
 }