public static bool TryParse(string value, out AlarmEvent alarmEvent)
        {
            alarmEvent = default;

            var match = EventRegEx.Match(value ?? string.Empty);

            if (match.Success && Enum.TryParse <EventType>(match.Groups["type"].Value, out var eventType))
            {
                alarmEvent = new AlarmEvent
                {
                    DateTime = DateTime.Parse($"{match.Groups["date"]} {match.Groups["time"]}"),
                    Type     = eventType,
                    Status   = match.Groups["status"].Value == "1",
                    Numbers  = match.Groups["numbers"].Value?.Split(',')
                               .Select(x => ushort.TryParse(x, out var num) ? num : (ushort?)null)
                               .Where(x => x.HasValue)
                               .Select(x => x.Value)
                               .ToArray() ?? Array.Empty <ushort>()
                };

                return(true);
            }

            return(false);
        }
        protected override async Task RunAsync(CancellationToken cancellationToken)
        {
            try
            {
                while (!cancellationToken.IsCancellationRequested)
                {
                    try
                    {
                        var settings = ConnectionSettings.Default;

                        var endpoint = new Uri($"http://{settings.Host}/cgi-bin/alarmchangestate_cgi?user={settings.Username}&pwd={settings.Password}&parameter=MotionDetection;SensorAlarm;SensorOutAlarm");

                        using var client = new HttpClient();

                        client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes($"{settings.Username}:{settings.Password}")));

                        client.Timeout = Timeout.InfiniteTimeSpan;

                        using var response = await client.SendAsync(new HttpRequestMessage (HttpMethod.Get, endpoint), HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);

                        using var body = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);

                        using var reader = new StreamReader(body);

                        while (!cancellationToken.IsCancellationRequested)
                        {
                            var line = await reader.ReadLineAsync().WithTimeout(TimeSpan.FromSeconds(15)).ConfigureAwait(false);

                            if (AlarmEvent.TryParse(line, out var alarmEvent))
                            {
                                try
                                {
                                    _events.OnNext(alarmEvent);
                                }
                                catch { }
                            }

                            await Task.Delay(100, cancellationToken).ConfigureAwait(false);
                        }
                    }
                    catch
                    {
                        await Task.Delay(RetryDelay, cancellationToken).ConfigureAwait(false);
                    }
                }
            }
            catch (OperationCanceledException)
            {
                return;
            }
        }