@Override
  public void onEvent(SensorEvent<Object> event) {
    if (firstUpTime == null) {
      if (event != null
          && Attributes.SERVICE_UP.equals(event.getSensor())
          && Boolean.TRUE.equals(event.getValue())) {
        firstUpTime = event.getTimestamp();
      } else if (event == null && Boolean.TRUE.equals(entity.getAttribute(Attributes.SERVICE_UP))) {
        // If this enricher is registered after the entity is up, then we'll get a "synthetic"
        // onEvent(null)
        firstUpTime = System.currentTimeMillis();
      }
    }

    super.onEvent(event);
  }
  @Override
  protected void setActualState(Lifecycle state) {
    long now = System.currentTimeMillis();

    synchronized (mutex) {
      if (state == Lifecycle.ON_FIRE) {
        if (lastPublished == LastPublished.FAILED) {
          if (currentRecoveryStartTime != null) {
            if (LOG.isDebugEnabled())
              LOG.debug(
                  "{} health-check for {}, component was recovering, now failing: {}",
                  new Object[] {this, entity, getExplanation(state)});
            currentRecoveryStartTime = null;
            publishEntityRecoveredTime = null;
          } else {
            if (LOG.isTraceEnabled())
              LOG.trace(
                  "{} health-check for {}, component still failed: {}",
                  new Object[] {this, entity, getExplanation(state)});
          }
        } else {
          if (firstUpTime == null && getConfig(ENTITY_FAILED_ONLY_IF_PREVIOUSLY_UP)) {
            // suppress; won't publish
          } else if (currentFailureStartTime == null) {
            if (LOG.isDebugEnabled())
              LOG.debug(
                  "{} health-check for {}, component now failing: {}",
                  new Object[] {this, entity, getExplanation(state)});
            currentFailureStartTime = now;
            publishEntityFailedTime =
                currentFailureStartTime
                    + getConfig(ENTITY_FAILED_STABILIZATION_DELAY).toMilliseconds();
          } else {
            if (LOG.isTraceEnabled())
              LOG.trace(
                  "{} health-check for {}, component continuing failing: {}",
                  new Object[] {this, entity, getExplanation(state)});
          }
        }
        if (setEntityOnFireTime == null) {
          setEntityOnFireTime =
              now + getConfig(SERVICE_ON_FIRE_STABILIZATION_DELAY).toMilliseconds();
        }
        currentRecoveryStartTime = null;
        publishEntityRecoveredTime = null;

      } else if (state == Lifecycle.RUNNING) {
        if (lastPublished == LastPublished.FAILED) {
          if (currentRecoveryStartTime == null) {
            if (LOG.isDebugEnabled())
              LOG.debug(
                  "{} health-check for {}, component now recovering: {}",
                  new Object[] {this, entity, getExplanation(state)});
            currentRecoveryStartTime = now;
            publishEntityRecoveredTime =
                currentRecoveryStartTime
                    + getConfig(ENTITY_RECOVERED_STABILIZATION_DELAY).toMilliseconds();
          } else {
            if (LOG.isTraceEnabled())
              LOG.trace(
                  "{} health-check for {}, component continuing recovering: {}",
                  new Object[] {this, entity, getExplanation(state)});
          }
        } else {
          if (currentFailureStartTime != null) {
            if (LOG.isDebugEnabled())
              LOG.debug(
                  "{} health-check for {}, component was failing, now healthy: {}",
                  new Object[] {this, entity, getExplanation(state)});
          } else {
            if (LOG.isTraceEnabled())
              LOG.trace(
                  "{} health-check for {}, component still healthy: {}",
                  new Object[] {this, entity, getExplanation(state)});
          }
        }
        currentFailureStartTime = null;
        publishEntityFailedTime = null;
        setEntityOnFireTime = null;

      } else {
        if (LOG.isTraceEnabled())
          LOG.trace(
              "{} health-check for {}, in unconfirmed sate: {}",
              new Object[] {this, entity, getExplanation(state)});
      }

      long recomputeIn = Long.MAX_VALUE; // For whether to call recomputeAfterDelay

      if (publishEntityFailedTime != null) {
        long delayBeforeCheck = publishEntityFailedTime - now;
        if (delayBeforeCheck <= 0) {
          if (LOG.isDebugEnabled())
            LOG.debug(
                "{} publishing failed (state={}; currentFailureStartTime={}; now={}",
                new Object[] {
                  this,
                  state,
                  Time.makeDateString(currentFailureStartTime),
                  Time.makeDateString(now)
                });
          publishEntityFailedTime = null;
          lastPublished = LastPublished.FAILED;
          entity.emit(
              HASensors.ENTITY_FAILED,
              new HASensors.FailureDescriptor(entity, getFailureDescription(now)));
        } else {
          recomputeIn = Math.min(recomputeIn, delayBeforeCheck);
        }
      } else if (publishEntityRecoveredTime != null) {
        long delayBeforeCheck = publishEntityRecoveredTime - now;
        if (delayBeforeCheck <= 0) {
          if (LOG.isDebugEnabled())
            LOG.debug(
                "{} publishing recovered (state={}; currentRecoveryStartTime={}; now={}",
                new Object[] {
                  this,
                  state,
                  Time.makeDateString(currentRecoveryStartTime),
                  Time.makeDateString(now)
                });
          publishEntityRecoveredTime = null;
          lastPublished = LastPublished.RECOVERED;
          entity.emit(HASensors.ENTITY_RECOVERED, new HASensors.FailureDescriptor(entity, null));
        } else {
          recomputeIn = Math.min(recomputeIn, delayBeforeCheck);
        }
      }

      if (setEntityOnFireTime != null) {
        long delayBeforeCheck = setEntityOnFireTime - now;
        if (delayBeforeCheck <= 0) {
          if (LOG.isDebugEnabled())
            LOG.debug(
                "{} setting on-fire, now that deferred period has passed (state={})",
                new Object[] {this, state});
          setEntityOnFireTime = null;
          super.setActualState(state);
        } else {
          recomputeIn = Math.min(recomputeIn, delayBeforeCheck);
        }
      } else {
        super.setActualState(state);
      }

      if (recomputeIn < Long.MAX_VALUE) {
        recomputeAfterDelay(recomputeIn);
      }
    }
  }