protected async Task <UponorWhoiseResponse> GetWhois(CancellationToken token)
        {
            UponorRequest request = new UponorRequest();

            request.Method = "whois";
            request.Id     = Interlocked.Increment(ref _nextRequestInteger);

            string json = JsonConvert.SerializeObject(request);

            using HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Post, "api");
            requestMessage.Content = new StringContent(json);

            HttpResponseMessage res = await SendRequest(requestMessage, token);

            string resJson = res.Content.ReadAsStringAsync().ConfigureAwait(false).GetAwaiter().GetResult();

            UponorResponse <UponorWhoiseResponse> resp = JsonConvert.DeserializeObject <UponorResponse <UponorWhoiseResponse> >(resJson);

            return(resp.Result);
        }
        protected async Task <object> GetAlarms(CancellationToken token = default)
        {
            throw new NotImplementedException();

            UponorRequest request = new UponorRequest();

            request.Method = "readactivealarms";
            request.Id     = Interlocked.Increment(ref _nextRequestInteger);

            string json = JsonConvert.SerializeObject(request);

            using HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Post, "api");
            requestMessage.Content = new StringContent(json);

            HttpResponseMessage res = await SendRequest(requestMessage, token);

            string resJson = res.Content.ReadAsStringAsync().ConfigureAwait(false).GetAwaiter().GetResult();

            // JS:
            //for (var i = 0; i < resArr.objects.length; i++) {
            //    var obj = resArr.objects[i];
            //    var state = obj.properties[self._alarmPropVal.state];
            //    var acked = obj.properties[self._alarmPropVal.acked];
            //    var name = obj.properties[self._alarmPropVal.name];
            //    values[obj.id] = {
            //        state: state && state.value > 0,
            //        acked: acked && acked.value > 0,
            //        name: name && name.value
            //    }
            //}

            // Other alarms, battery etc.
            //var alarms = [];
            //if (utils.isAlarmActive(c.api.zoneAlarm(unitNo, zoneNo, 'battery_alarm'))) {
            //    alarms.push('images img-alarm-batt');
            //}
            //if (utils.isAlarmActive(c.api.zoneAlarm(unitNo, zoneNo, 'rf_alarm'))) {
            //    alarms.push('images img-alarm-signal');
            //}
            //if (utils.isAlarmActive(c.api.zoneAlarm(unitNo, zoneNo, 'technical_alarm'))) {
            //    alarms.push('images img-alarm-tech');
            //}
            //if (utils.isAlarmActive(c.api.zoneAlarm(unitNo, zoneNo, 'tamper_indication'))) {
            //    alarms.push('images img-alarm-tech');
            //}

            //// add alarm row if we have active alarms of rh protection is active
            //if (alarms.length > 0) {
            //    var tr = addRow(tbRooms);
            //    tr.addClass('clickable_alarm_row');
            //    addCell(tr, {text: c.api.zoneValue(unitNo, zoneNo, 'room_name')});
            //    addCell(tr, {icons: alarms});

            //    var td = $C('td').attr('align', 'right').appendTo(tr);
            //        $C('i').addClass('images img-arrow-right').appendTo(td);
            //    addCell(tr, {element: td});

            //    tr.click(function () {
            //        a.openScreenUZ(unitNo, zoneNo, screens.alarms);
            //    });
            //}

            UponorResponse <UponorWhoiseResponse> resp = JsonConvert.DeserializeObject <UponorResponse <UponorWhoiseResponse> >(resJson);

            return(resp.Result);
        }
        public async Task <UponorResponseContainer> ReadValues(IEnumerable <int> objects, IEnumerable <UponorProperties> properties, CancellationToken token = default)
        {
            ICollection <UponorProperties> propsList = properties as ICollection <UponorProperties> ?? properties.ToList();

            List <List <int> > batches = new List <List <int> >();
            List <int>         current = new List <int>();

            batches.Add(current);

            int objectsPerRequest = (int)Math.Max(1, Math.Round(PropertiesPerRequest * 1f / propsList.Count));

            foreach (int o in objects)
            {
                current.Add(o);
                if (current.Count >= objectsPerRequest)
                {
                    batches.Add(current = new List <int>());
                }
            }

            UponorResponseContainer            responseContainer = new UponorResponseContainer();
            List <Task <HttpResponseMessage> > tasks             = new List <Task <HttpResponseMessage> >();

            if (batches.All(x => !x.Any()))
            {
                // Early exit, if no requests are to be made
                return(responseContainer);
            }

            foreach (List <int> batch in batches)
            {
                UponorRequest request = new UponorRequest();
                request.Id = Interlocked.Increment(ref _nextRequestInteger);

                foreach (int objectId in batch)
                {
                    UponorObject obj = new UponorObject();
                    obj.Id = objectId.ToString();

                    foreach (UponorProperties property in propsList)
                    {
                        obj.Properties.Add((int)property, UponorValueContainer.EmptyValueContainer);
                    }

                    request.Params.Objects.Add(obj);
                }

                string json = JsonConvert.SerializeObject(request);

                HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Post, "api");
                requestMessage.Content = new StringContent(json);

                Task <HttpResponseMessage> requestTask = SendRequest(requestMessage, token);
                tasks.Add(requestTask);
            }

            // Await each response
            foreach (Task <HttpResponseMessage> task in tasks)
            {
                HttpResponseMessage res = await task;
                string resJson          = res.Content.ReadAsStringAsync().ConfigureAwait(false).GetAwaiter().GetResult();

                // Hack json to handle empty properties
                resJson = resJson.Replace(":}", ":\"\"}");

                UponorResponse <UponorParams> resp = JsonConvert.DeserializeObject <UponorResponse <UponorParams> >(resJson);

                foreach (UponorObject o in resp.Result.Objects)
                {
                    foreach (KeyValuePair <int, UponorValueContainer> prop in o.Properties)
                    {
                        responseContainer.AddResponse(Convert.ToInt32(o.Id), (UponorProperties)prop.Key, prop.Value.Value);
                    }
                }
            }

            return(responseContainer);
        }