void patternAnimator_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker = (BackgroundWorker)sender;

            try
            {
                log.InfoFormat("[{0}] Starting pattern playback", this.Serial);

                List <TriggeredEvent> eventsPlaying = new List <TriggeredEvent>();

                while (!worker.CancellationPending)
                {
                    TriggeredEvent ev = null;

                    lock (EventQueue)
                    {
                        for (int i = 0; i < EventQueue.Count; i++)
                        {
                            ev = EventQueue.Dequeue();

                            for (int j = eventsPlaying.Count - 1; j >= 0; j--)
                            {
                                if (ev.Notification == eventsPlaying[j].Notification && (eventsPlaying[j].Repeat < 0 || eventsPlaying[j].Duration > 0))
                                {
                                    log.DebugFormat("Removing infinite playback notifications as there is another pending");
                                    TriggeredEvent evPlaying = eventsPlaying[j];
                                    eventsPlaying.RemoveAt(j);
                                    AssignBusyLeds(evPlaying, false);
                                }
                            }

                            if (CanPlayEvent(ev))
                            {
                                break;
                            }
                            else
                            {
                                EventQueue.Enqueue(ev);
                                ev = null;
                            }
                        }
                    }

                    if (ev != null)
                    {
                        if (ev.NotificationSnapshot is PatternNotification)
                        {
                            ev.EventStarted     = DateTime.Now;
                            ev.AnimationStarted = DateTime.Now;

                            PatternNotification notification = ev.NotificationSnapshot as PatternNotification;
                            foreach (Animation animation in notification.Pattern.Animations)
                            {
                                Animation copyAnimation = new Animation();
                                copyAnimation.Assign(animation);
                                ev.Animations.Add(copyAnimation);
                            }

                            ev.Animations[0].ReferenceColor = GetColor(ev.NotificationSnapshot);

                            AssignBusyLeds(ev, true);
                            eventsPlaying.Add(ev);
                        }
                        else
                        {
                            ev.AnimationStarted = DateTime.Now;
                            ev.EventStarted     = DateTime.Now;

                            foreach (Animation animation in ev.Pattern.Animations)
                            {
                                Animation copyAnimation = new Animation();
                                copyAnimation.Assign(animation);
                                ev.Animations.Add(copyAnimation);
                            }

                            ev.Animations[0].ReferenceColor = GetColor(ev.NotificationSnapshot);

                            AssignBusyLeds(ev, true);
                            eventsPlaying.Add(ev);
                        }
                    }

                    //Prepare next frame of data to send to LEDs
                    for (int ii = eventsPlaying.Count - 1; ii >= 0; ii--)
                    {
                        TriggeredEvent evnt = eventsPlaying[ii];

                        RgbColor color = evnt.Animations[evnt.AnimationIndex].GetColor(evnt.AnimationStarted.Value, DateTime.Now, evnt.Animations[evnt.AnimationIndex].ReferenceColor);

                        PatternNotification notification = evnt.NotificationSnapshot as PatternNotification;

                        lock (this)
                        {
                            SetColor(evnt, color.R, color.G, color.B);
                        }

                        if (evnt.Animations[evnt.AnimationIndex].AnimationFinished)
                        {
                            evnt.AnimationIndex += 1;

                            if (evnt.Duration > 0 && evnt.EventStarted.Value.AddMilliseconds(evnt.Duration) <= DateTime.Now)
                            {
                                eventsPlaying.RemoveAt(ii);
                                AssignBusyLeds(evnt, false);
                            }
                            else if (evnt.AnimationIndex == evnt.Animations.Count)
                            {
                                evnt.RepeatCount += 1;

                                if (evnt.Duration > 0 || evnt.Repeat < 0 || evnt.RepeatCount < evnt.Repeat)
                                {
                                    evnt.AnimationStarted = DateTime.Now;
                                    evnt.AnimationIndex   = 0;

                                    evnt.Animations.ForEach(delegate(Animation a) { a.Reset(); });
                                }
                                else
                                {
                                    eventsPlaying.RemoveAt(ii);
                                    AssignBusyLeds(evnt, false);
                                }
                            }
                            else
                            {
                                evnt.Animations[evnt.AnimationIndex].ReferenceColor = GetColor(evnt.NotificationSnapshot);
                                evnt.AnimationStarted = DateTime.Now;
                            }
                        }
                    }

                    if (Led != null && NeedsLedUpdate)
                    {
                        lock (this)
                        {
                            NeedsLedUpdate = false;
                        }

                        int retryCount = 5;

                        if (Led.BlinkStickDevice == BlinkStickDeviceEnum.BlinkStick ||
                            Led.BlinkStickDevice == BlinkStickDeviceEnum.BlinkStickPro && Led.Mode < 2)
                        {
                            while (retryCount > 0)                              //Retry loop
                            {
                                try
                                {
                                    Led.SetColor(LedFrame[0][1], LedFrame[0][0], LedFrame[0][2]);
                                    retryCount = 0;
                                }
                                catch (Exception ex)
                                {
                                    retryCount--;
                                    log.ErrorFormat("Failed to set color: {0}", ex);
                                    if (retryCount > 0)
                                    {
                                        Thread.Sleep(20);
                                        log.InfoFormat("Retry set color #{0}", 5 - retryCount);
                                    }
                                    else
                                    {
                                        log.Warn("Failed to set color 5 times, giving up...");
                                    }
                                }
                            }
                        }
                        else
                        {
                            byte[] frame = new byte[this.LedsR * 3];
                            Array.Copy(LedFrame[0], 0, frame, 0, frame.Length);

                            while (retryCount > 0)                             //Retry loop
                            {
                                try
                                {
                                    Led.SetColors(0, frame);
                                    retryCount = 0;
                                }
                                catch (Exception ex)
                                {
                                    retryCount--;
                                    log.ErrorFormat("Failed to set color: {0}", ex);
                                    if (retryCount > 0)
                                    {
                                        Thread.Sleep(20);
                                        log.InfoFormat("Retry set color #{0}", 5 - retryCount);
                                    }
                                    else
                                    {
                                        log.Warn("Failed to set color 5 times, giving up...");
                                    }
                                }
                            }

                            int sleep = Math.Max(2, (int)(this.LedsR * 3 * 8f / 400f * 1.2)); //number of LEDs times 3 color elements times 8 bytes divided by speed
                            Thread.Sleep(sleep);

                            if (Led != null && Led.BlinkStickDevice == BlinkStickDeviceEnum.BlinkStickPro)
                            {
                                frame = new byte[this.LedsG * 3];
                                Array.Copy(LedFrame[1], 0, frame, 0, frame.Length);

                                while (retryCount > 0)                                 //Retry loop
                                {
                                    try
                                    {
                                        Led.SetColors(1, frame);
                                        retryCount = 0;
                                    }
                                    catch (Exception ex)
                                    {
                                        retryCount--;
                                        log.ErrorFormat("Failed to set color: {0}", ex);
                                        if (retryCount > 0)
                                        {
                                            Thread.Sleep(20);
                                            log.InfoFormat("Retry set color #{0}", 5 - retryCount);
                                        }
                                        else
                                        {
                                            log.Warn("Failed to set color 5 times, giving up...");
                                        }
                                    }
                                }

                                sleep = Math.Max(2, (int)(this.LedsG * 3 * 8f / 400f * 1.2));
                                Thread.Sleep(sleep);

                                frame = new byte[this.LedsB * 3];
                                Array.Copy(LedFrame[2], 0, frame, 0, frame.Length);

                                while (retryCount > 0)                                 //Retry loop
                                {
                                    try
                                    {
                                        Led.SetColors(2, frame);
                                        retryCount = 0;
                                    }
                                    catch (Exception ex)
                                    {
                                        retryCount--;
                                        log.ErrorFormat("Failed to set color: {0}", ex);
                                        if (retryCount > 0)
                                        {
                                            Thread.Sleep(20);
                                            log.InfoFormat("Retry set color #{0}", 5 - retryCount);
                                        }
                                        else
                                        {
                                            log.Warn("Failed to set color 5 times, giving up...");
                                        }
                                    }
                                }

                                sleep = Math.Max(2, (int)(this.LedsB * 3 * 8f / 400f * 1.2));
                                Thread.Sleep(sleep);
                            }
                        }
                    }
                    else
                    {
                        Thread.Sleep(1);
                    }
                }
            }
            catch (Exception ex)
            {
                log.ErrorFormat("Pattern playback crash {0}", ex);
            }

            log.InfoFormat("[{0}] Pattern playback stopped", this.Serial);

            Running = false;
        }