     }
   }
 
-  delay(100);
-}
+  if (command_queue) {
+    CommandEntry* command = command_queue;
+    if (FADE_COMMAND == command->command) {
+      current_R += (((int)(command->R) - current_R) * DELAY_MILLIS)/command->duration;
+      current_G += (((int)(command->G) - current_G) * DELAY_MILLIS)/command->duration;
+      current_B += (((int)(command->B) - current_B) * DELAY_MILLIS)/command->duration;
+      for(uint16_t i = command->range_start; i <= command->range_end; i++) {
+        strip.setPixelColor(i, strip.Color(current_R, current_G, current_B));
+      }
+      strip.show();
+      if ((command->duration - DELAY_MILLIS) > 0) {
+        command->duration = command->duration - DELAY_MILLIS;
+      } else {
+        // we are done
+        delete(dequeueHead());
+      }
+    }
+  }
+
+  delay(DELAY_MILLIS);
+}

PubSubClient client(mqttServerString, mqttServerPort, callback, wclient);

void setup() {
  strip.begin();
  for(uint16_t i=0; i <strip.numPixels(); i++) {
    strip.setPixelColor(i, strip.Color(0,0,0));

const char* OFF = "off";
const char* RANGE = "range";
const char* CLEAR_AND_RANGE = "clear+range";
const char* FADE = "fade";

#define FADE_COMMAND 1

uint8_t current_R = 0;
uint8_t current_G = 0;
uint8_t current_B = 0;
 
void setCurrent(uint8_t R, uint8_t G, int8_t B) {
  current_R = R;
  current_B = B;
  current_B = G;
}

struct CommandEntry {
  int command;
  int range_start;
  int range_end;
  uint8_t R;
  uint8_t G;
  uint8_t B;
  int duration;
  CommandEntry* next_command;
  CommandEntry* next_in_queue;
};

CommandEntry* command_queue = nullptr;

void enqueueCommand(struct CommandEntry* command) {
  command->next_in_queue = nullptr;
  if (command_queue == nullptr) {
    command_queue = command;
    return;
  }
  CommandEntry* tail = command_queue;
  while(tail->next_in_queue != nullptr) {
    tail = tail->next_in_queue;
  }
  tail->next_in_queue = command;
}

CommandEntry* dequeueHead() {
  if (nullptr != command_queue) {
    CommandEntry* head = command_queue;
    command_queue = command_queue->next_in_queue;
    return head;
  }
  return nullptr;
}

void clearQueue() {
  CommandEntry* head = dequeueHead();
  while(head != nullptr) {
    delete(head);
    head = dequeueHead();
  }
}

boolean extractRangeAndTarget(const char* buffer,
                              int* start,
