InfluxDB.Client 4.8.0-dev.9324

.NET Standard 2.0
This is a prerelease version of InfluxDB.Client.
There is a newer version of this package available.
See the version list below for details.
dotnet add package InfluxDB.Client --version 4.8.0-dev.9324
NuGet\Install-Package InfluxDB.Client -Version 4.8.0-dev.9324
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="InfluxDB.Client" Version="4.8.0-dev.9324" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add InfluxDB.Client --version 4.8.0-dev.9324
#r "nuget: InfluxDB.Client, 4.8.0-dev.9324"
#r directive can be used in F# Interactive, C# scripting and .NET Interactive. Copy this into the interactive tool or source code of the script to reference the package.
// Install InfluxDB.Client as a Cake Addin
#addin nuget:?package=InfluxDB.Client&version=4.8.0-dev.9324&prerelease

// Install InfluxDB.Client as a Cake Tool
#tool nuget:?package=InfluxDB.Client&version=4.8.0-dev.9324&prerelease

InfluxDB.Client

CircleCI

The reference client that allows query, write and management (bucket, organization, users) for the InfluxDB 2.x.

Documentation

This section contains links to the client library documentation.

Features

Queries

For querying data we use QueryApi that allow perform asynchronous, streaming, synchronous and also use raw query response.

Asynchronous Query

The asynchronous query is not intended for large query results because the Flux response can be potentially unbound.

using System;
using System.Threading.Tasks;
using InfluxDB.Client;

namespace Examples
{
    public static class AsynchronousQuery
    {
        private static readonly string Token = "";

        public static async Task Main()
        {
            using var client = new InfluxDBClient("http://localhost:8086", Token);

            var flux = "from(bucket:\"temperature-sensors\") |> range(start: 0)";
            
            var queryApi = client.GetQueryApi();

            //
            // QueryData
            //
            var tables = await queryApi.QueryAsync(flux, "org_id");
            tables.ForEach(table =>
            {
                table.Records.ForEach(record =>
                {
                    Console.WriteLine($"{record.GetTime()}: {record.GetValueByKey("_value")}");
                });
            });
        }        
    }
}

The asynchronous query offers a possibility map FluxRecords to POCO:

using System;
using System.Threading.Tasks;
using InfluxDB.Client;
using InfluxDB.Client.Core;

namespace Examples
{
    public static class AsynchronousQuery
    {
        private static readonly string Token = "";

        public static async Task Main()
        {
            using var client = new InfluxDBClient("http://localhost:8086", Token);

            var flux = "from(bucket:\"temperature-sensors\") |> range(start: 0)";
            
            var queryApi = client.GetQueryApi();

            //
            // QueryData
            //
            var temperatures = await queryApi.QueryAsync<Temperature>(flux, "org_id");
            temperatures.ForEach(temperature =>
            {
                Console.WriteLine($"{temperature.Location}: {temperature.Value} at {temperature.Time}");
            });
        }  
        
        [Measurement("temperature")]
        private class Temperature
        {
            [Column("location", IsTag = true)] public string Location { get; set; }

            [Column("value")] public double Value { get; set; }

            [Column(IsTimestamp = true)] public DateTime Time { get; set; }
        }
    }
}

Streaming Query

The Streaming query offers possibility to process unbound query and allow user to handle exceptions, stop receiving more results and notify that all data arrived.

using System;
using System.Threading.Tasks;
using InfluxDB.Client;

namespace Examples
{
    public static class StreamingQuery
    {
        private static readonly string Token = "";

        public static async Task Main()
        {
            using var client = new InfluxDBClient("http://localhost:8086", Token);

            var flux = "from(bucket:\"temperature-sensors\") |> range(start: 0)";

            var queryApi = client.GetQueryApi();

            //
            // QueryData
            //
            await queryApi.QueryAsync(flux, record =>
            {
                //
                // The callback to consume a FluxRecord.
                //
                Console.WriteLine($"{record.GetTime()}: {record.GetValueByKey("_value")}");
            }, exception =>
            {
                //
                // The callback to consume any error notification.
                //
                Console.WriteLine($"Error occurred: {exception.Message}");
            }, () =>
            {
                //
                // The callback to consume a notification about successfully end of stream.
                //
                Console.WriteLine("Query completed");
            }, "org_id");
        }
    }
}

And there is also a possibility map FluxRecords to POCO:

using System;
using System.Threading.Tasks;
using InfluxDB.Client;
using InfluxDB.Client.Core;

namespace Examples
{
    public static class StreamingQuery
    {
        private static readonly string Token = "";

        public static async Task Main()
        {
            using var client = new InfluxDBClient("http://localhost:8086", Token);

            var flux = "from(bucket:\"temperature-sensors\") |> range(start: 0)";
            
            var queryApi = client.GetQueryApi();

            //
            // QueryData
            //
            await queryApi.QueryAsync<Temperature>(flux, temperature =>
            {
                //
                // The callback to consume a FluxRecord mapped to POCO.
                //
                Console.WriteLine($"{temperature.Location}: {temperature.Value} at {temperature.Time}");
            }, org: "org_id");
        }  
        
        [Measurement("temperature")]
        private class Temperature
        {
            [Column("location", IsTag = true)] public string Location { get; set; }

            [Column("value")] public double Value { get; set; }

            [Column(IsTimestamp = true)] public DateTime Time { get; set; }
        }
    }
}

Raw Query

The Raw query allows direct processing original CSV response:

using System;
using System.Threading.Tasks;
using InfluxDB.Client;

namespace Examples
{
    public static class RawQuery
    {
        private static readonly string Token = "";

        public static async Task Main()
        {
            using var client = new InfluxDBClient("http://localhost:8086", Token);

            var flux = "from(bucket:\"temperature-sensors\") |> range(start: 0)";

            var queryApi = client.GetQueryApi();

            //
            // QueryData
            //
            var csv = await queryApi.QueryRawAsync(flux, org: "org_id");
            
            Console.WriteLine($"CSV response: {csv}");
        }
    }
}

The Streaming version allows processing line by line:

using System;
using System.Threading.Tasks;
using InfluxDB.Client;

namespace Examples
{
    public static class RawQueryAsynchronous
    {
        private static readonly string Token = "";

        public static async Task Main()
        {
            using var client = new InfluxDBClient("http://localhost:8086", Token);

            var flux = "from(bucket:\"temperature-sensors\") |> range(start: 0)";

            var queryApi = client.GetQueryApi();

            //
            // QueryData
            //
            await queryApi.QueryRawAsync(flux, line =>
            {
                //
                // The callback to consume a line of CSV response
                //
                Console.WriteLine($"Response: {line}");
            }, org: "org_id");
        }
    }
}

Synchronous query

The synchronous query is not intended for large query results because the response can be potentially unbound.

using System;
using InfluxDB.Client;

namespace Examples
{
    public static class SynchronousQuery
    {
        public static void Main()
        {
            using var client = new InfluxDBClient("http://localhost:9999", "my-token");

            const string query = "from(bucket:\"my-bucket\") |> range(start: 0)";
           
            //
            // QueryData
            //
            var queryApi = client.GetQueryApiSync();
            var tables = queryApi.QuerySync(query, "my-org");
            
            //
            // Process results
            //
            tables.ForEach(table =>
            {
                table.Records.ForEach(record =>
                {
                    Console.WriteLine($"{record.GetTime()}: {record.GetValueByKey("_value")}");
                });
            });
        }
    }
}

Writes

For writing data we use WriteApi or WriteApiAsync which is simplified version of WriteApi without batching support.

WriteApi supports:

  1. writing data using InfluxDB Line Protocol, Data Point, POCO
  2. use batching for writes
  3. produces events that allow user to be notified and react to this events
    • WriteSuccessEvent - published when arrived the success response from server
    • WriteErrorEvent - published when occurs a unhandled exception from server
    • WriteRetriableErrorEvent - published when occurs a retriable error from server
    • WriteRuntimeExceptionEvent - published when occurs a runtime exception in background batch processing
  4. use GZIP compression for data

The writes are processed in batches which are configurable by WriteOptions:

Property Description Default Value
BatchSize the number of data point to collect in batch 1000
FlushInterval the number of milliseconds before the batch is written 1000
JitterInterval the number of milliseconds to increase the batch flush interval by a random amount 0
RetryInterval the number of milliseconds to retry unsuccessful write. The retry interval is used when the InfluxDB server does not specify "Retry-After" header. 5000
MaxRetries the number of max retries when write fails 3
MaxRetryDelay the maximum delay between each retry attempt in milliseconds 125_000
ExponentialBase the base for the exponential retry delay, the next delay is computed using random exponential backoff as a random value within the interval retryInterval * exponentialBase^(attempts-1) and retryInterval * exponentialBase^(attempts). Example for retryInterval=5_000, exponentialBase=2, maxRetryDelay=125_000, maxRetries=5 Retry delays are random distributed values within the ranges of [5_000-10_000, 10_000-20_000, 20_000-40_000, 40_000-80_000, 80_000-125_000] 2

Writing data

By POCO

Write Measurement into specified bucket:

using System;
using InfluxDB.Client;
using InfluxDB.Client.Api.Domain;
using InfluxDB.Client.Core;

namespace Examples
{
    public static class WritePoco
    {
        private static readonly string Token = "";

        public static void Main()
        {
            using var client = new InfluxDBClient("http://localhost:8086", Token);

            //
            // Write Data
            //
            using (var writeApi = client.GetWriteApi())
            {
                //
                // Write by POCO
                //
                var temperature = new Temperature {Location = "south", Value = 62D, Time = DateTime.UtcNow};

                writeApi.WriteMeasurement(temperature, WritePrecision.Ns, "bucket_name", "org_id");
            }
        }
        
        [Measurement("temperature")]
        private class Temperature
        {
            [Column("location", IsTag = true)] public string Location { get; set; }

            [Column("value")] public double Value { get; set; }

            [Column(IsTimestamp = true)] public DateTime Time { get; set; }
        }
    }
}
By Data Point

Write Data point into specified bucket:

using System;
using InfluxDB.Client;
using InfluxDB.Client.Api.Domain;
using InfluxDB.Client.Writes;

namespace Examples
{
    public static class WriteDataPoint
    {
        private static readonly string Token = "";

        public static void Main()
        {
            using var client = new InfluxDBClient("http://localhost:8086", Token);

            //
            // Write Data
            //
            using (var writeApi = client.GetWriteApi())
            {
                //
                // Write by Data Point
                
                var point = PointData.Measurement("temperature")
                    .Tag("location", "west")
                    .Field("value", 55D)
                    .Timestamp(DateTime.UtcNow.AddSeconds(-10), WritePrecision.Ns);
                
                writeApi.WritePoint(point, "bucket_name", "org_id");
            }
        }
    }
}

DataPoint Builder Immutability: The builder is immutable therefore won't have side effect when using for building multiple point with single builder.

using System;
using InfluxDB.Client;
using InfluxDB.Client.Api.Domain;
using InfluxDB.Client.Writes;

namespace Examples
{
    public static class WriteDataPoint
    {
        private static readonly string Token = "";

        public static void Main()
        {
            using var client = new InfluxDBClient("http://localhost:8086", Token);

            //
            // Write Data
            //
            using (var writeApi = client.GetWriteApi())
            {
                //
                // Write by Data Point
                
                var builder = PointData.Measurement("temperature")
                    .Tag("location", "west");
                
                var pointA = builder
                    .Field("value", 55D)
                    .Timestamp(DateTime.UtcNow.AddSeconds(-10), WritePrecision.Ns);
                
                writeApi.WritePoint(pointA, "bucket_name", "org_id");
                
                var pointB = builder
                    .Field("age", 32)
                    .Timestamp(DateTime.UtcNow, WritePrecision.Ns);
                
                writeApi.WritePoint(pointB, "bucket_name", "org_id");
            }
        }
    }
}
By LineProtocol

Write Line Protocol record into specified bucket:

using InfluxDB.Client;
using InfluxDB.Client.Api.Domain;

namespace Examples
{
    public static class WriteLineProtocol
    {
        private static readonly string Token = "";

        public static void Main()
        {
            using var client = new InfluxDBClient("http://localhost:8086", Token);

            //
            // Write Data
            //
            using (var writeApi = client.GetWriteApi())
            {
                //
                //
                // Write by LineProtocol
                //
                writeApi.WriteRecord("temperature,location=north value=60.0", WritePrecision.Ns,"bucket_name", "org_id");
            }
        }
    }
}
Using WriteApiAsync
using System;
using System.Threading.Tasks;
using InfluxDB.Client;
using InfluxDB.Client.Api.Domain;
using InfluxDB.Client.Core;
using InfluxDB.Client.Writes;

namespace Examples
{
    public static class WriteApiAsyncExample
    {   
        [Measurement("temperature")]
        private class Temperature
        {
            [Column("location", IsTag = true)] public string Location { get; set; }

            [Column("value")] public double Value { get; set; }

            [Column(IsTimestamp = true)] public DateTime Time { get; set; }
        }
        
        public static async Task Main()
        {
            using var client = new InfluxDBClient("http://localhost:8086", 
                            "my-user", "my-password");

            //
            // Write Data
            //
            var writeApiAsync = client.GetWriteApiAsync();

            //
            //
            // Write by LineProtocol
            //
            await writeApiAsync.WriteRecordAsync("temperature,location=north value=60.0", WritePrecision.Ns,
                "my-bucket", "my-org");

            //
            //
            // Write by Data Point
            //               
            var point = PointData.Measurement("temperature")
                            .Tag("location", "west")
                            .Field("value", 55D)
                            .Timestamp(DateTime.UtcNow.AddSeconds(-10), WritePrecision.Ns);

            await writeApiAsync.WritePointAsync(point, "my-bucket", "my-org");

            //
            // Write by POCO
            //
            var temperature = new Temperature {Location = "south", Value = 62D, Time = DateTime.UtcNow};

            await writeApiAsync.WriteMeasurementAsync(temperature, WritePrecision.Ns, "my-bucket", "my-org");

            //
            // Check written data
            //
            var tables = await influxDbClient.GetQueryApi()
                            .QueryAsync("from(bucket:\"my-bucket\") |> range(start: 0)", "my-org");
            
            tables.ForEach(table =>
            {
                var fluxRecords = table.Records;
                fluxRecords.ForEach(record =>
                {
                    Console.WriteLine($"{record.GetTime()}: {record.GetValue()}");
                });
            });
        }
    }
}
Default Tags

Sometimes is useful to store same information in every measurement e.g. hostname, location, customer. The client is able to use static value, app settings or env variable as a tag value.

The expressions:

  • California Miner - static value
  • ${version} - application settings
  • ${env.hostname} - environment property
Via Configuration file

In a configuration file you are able to specify default tags by tags element.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <configSections>
        <section name="influx2" type="InfluxDB.Client.Configurations.Influx2, InfluxDB.Client" />
    </configSections>
    <appSettings>
        <add key="SensorVersion" value="v1.00"/>
    </appSettings>
    <influx2 url="http://localhost:8086"
             org="my-org"
             bucket="my-bucket"
             token="my-token"
             logLevel="BODY"
             timeout="10s">
        <tags>
            <tag name="id" value="132-987-655"/>
            <tag name="customer" value="California Miner"/>
            <tag name="hostname" value="${env.Hostname}"/>
            <tag name="sensor-version" value="${SensorVersion}"/>
        </tags>
    </influx2>
</configuration>
Via API
var options = new InfluxDBClientOptions(Url)
{
    Token = token,
    DefaultTags = new Dictionary<string, string>
    {
        {"id", "132-987-655"},
        {"customer", "California Miner"},
    }
};   
options.AddDefaultTag("hostname", "${env.Hostname}")
options.AddDefaultTags(new Dictionary<string, string>{{ "sensor-version", "${SensorVersion}" }})

Both of configurations will produce the Line protocol:

mine-sensor,id=132-987-655,customer="California Miner",hostname=example.com,sensor-version=v1.00 altitude=10

Handle the Events

Events that can be handle by WriteAPI EventHandler are:

  • WriteSuccessEvent - for success response from server
  • WriteErrorEvent - for unhandled exception from server
  • WriteRetriableErrorEvent - for retriable error from server
  • WriteRuntimeExceptionEvent - for runtime exception in background batch processing

Number of events depends on number of data points to collect in batch. The batch size is configured by BatchSize option (default size is 1000) - in case of one data point, event is handled for each point, independently on used writing method (even for mass writing of data like WriteMeasurements, WritePoints and WriteRecords).

Events can be handled by register writeApi.EventHandler or by creating custom EventListener:

Register EventHandler
writeApi.EventHandler += (sender, eventArgs) =>
{
    switch (eventArgs)
    {
        case WriteSuccessEvent successEvent:
            string data = @event.LineProtocol;
            //
            // handle success response from server
            // Console.WriteLine($"{data}");
            //
            break;
        case WriteErrorEvent error:
            string data = @error.LineProtocol;
            string errorMessage = @error.Exception.Message;
            //
            // handle unhandled exception from server
            //
            // Console.WriteLine($"{data}");
            // throw new Exception(errorMessage);
            //
            break;
        case WriteRetriableErrorEvent error:
            string data = @error.LineProtocol;
            string errorMessage = @error.Exception.Message;
            //
            // handle retrievable error from server
            //
            // Console.WriteLine($"{data}");
            // throw new Exception(errorMessage);
            //
            break;
        case WriteRuntimeExceptionEvent error:
            string errorMessage = @error.Exception.Message;
            //
            // handle runtime exception in background batch processing
            // throw new Exception(errorMessage);
            //
            break;
    }
};

//
// Write by LineProtocol
//
writeApi.WriteRecord("influxPoint,writeType=lineProtocol value=11.11" +
    $" {DateTime.UtcNow.Subtract(EpochStart).Ticks * 100}", WritePrecision.Ns, "my-bucket", "my-org");
Custom EventListener

Advantage of using custom Event Listener is possibility of waiting on handled event between different writings - for more info see EventListener.

Delete Data

Delete data from specified bucket:

using InfluxDB.Client;
using InfluxDB.Client.Api.Domain;

namespace Examples
{
    public static class WriteLineProtocol
    {
        private static readonly string Token = "";

        public static void Main()
        {
            using var client = new InfluxDBClient("http://localhost:8086", Token);

            //
            // Delete data
            //
            await client.GetDeleteApi().Delete(DateTime.UtcNow.AddMinutes(-1), DateTime.Now, "", "bucket", "org");
        }
    }
}

Management API

The client has following management API:

API endpoint Description Implementation
/api/v2/authorizations Managing authorization data AuthorizationsApi
/api/v2/buckets Managing bucket data BucketsApi
/api/v2/orgs Managing organization data OrganizationsApi
/api/v2/users Managing user data UsersApi
/api/v2/sources Managing sources SourcesApi
/api/v2/tasks Managing one-off and recurring tasks TasksApi
/api/v2/scrapers Managing ScraperTarget data ScraperTargetsApi
/api/v2/labels Managing resource labels LabelsApi
/api/v2/telegrafs Managing telegraf config data TelegrafsApi
/api/v2/setup Managing onboarding setup InfluxDBClient#OnBoarding()
/ready Get the readiness of a instance at startup InfluxDBClient#Ready()
/health Get the health of an instance anytime during execution InfluxDBClient#Health()

The following example demonstrates how to use a InfluxDB 2.x Management API. For further information see endpoints implementation.

using System;
using System.Collections.Generic;
using System.Linq;
using InfluxDB.Client;
using InfluxDB.Client.Api.Domain;
using Task = System.Threading.Tasks.Task;

namespace Examples
{
    public static class ManagementExample
    {
        public static async Task Main()
        {
            const string url = "http://localhost:8086";
            const string token = "my-token";
            const string org = "my-org";
            
            using var client = new InfluxDBClient(url, token);

            // Find ID of Organization with specified name (PermissionAPI requires ID of Organization).
            var orgId = (await client.GetOrganizationsApi().FindOrganizationsAsync(org: org)).First().Id;

            //
            // Create bucket "iot_bucket" with data retention set to 3,600 seconds
            //
            var retention = new BucketRetentionRules(BucketRetentionRules.TypeEnum.Expire, 3600);

            var bucket = await client.GetBucketsApi().CreateBucketAsync("iot_bucket", retention, orgId);

            //
            // Create access token to "iot_bucket"
            //
            var resource = new PermissionResource(PermissionResource.TypeBuckets, bucket.Id, null,
                orgId);

            // Read permission
            var read = new Permission(Permission.ActionEnum.Read, resource);

            // Write permission
            var write = new Permission(Permission.ActionEnum.Write, resource);

            var authorization = await client.GetAuthorizationsApi()
                .CreateAuthorizationAsync(orgId, new List<Permission> { read, write });

            //
            // Created token that can be use for writes to "iot_bucket"
            //
            Console.WriteLine($"Authorized token to write into iot_bucket: {authorization.Token}");
        }
    }
}

If there is no API implementation for particular service you could create the service by:

var dbrpService = _client.CreateService<DBRPsService>(typeof(DBRPsService));

Advanced Usage

Monitoring & Alerting

The example below show how to create a check for monitoring a stock price. A Slack notification is created if the price is lesser than 35.

Create Threshold Check

The Check set status to Critical if the current value for a stock measurement is lesser than 35.

var org = ...;

var query = "from(bucket: \"my-bucket\") "
        + "|> range(start: v.timeRangeStart, stop: v.timeRangeStop)  "
        + "|> filter(fn: (r) => r._measurement == \"stock\")  "
        + "|> filter(fn: (r) => r.company == \"zyz\")  "
        + "|> aggregateWindow(every: 5s, fn: mean)  "
        + "|> filter(fn: (r) => r._field == \"current\")  "
        + "|> yield(name: \"mean\")";

var threshold = new LesserThreshold(value: 35F, level: CheckStatusLevel.CRIT,
                type: LesserThreshold.TypeEnum.Lesser);

var message = "The Stock price for XYZ is on: ${ r._level } level!";

await Client
    .GetChecksApi()
    .CreateThresholdCheckAsync("XYZ Stock value", query, "5s", message, threshold, org.Id);
Create Slack Notification endpoint
var url = "https://hooks.slack.com/services/x/y/z"; 

var endpoint = await Client
    .GetNotificationEndpointsApi()
    .CreateSlackEndpointAsync("Slack Endpoint", url, org.Id);
Create Notification Rule
await Client
    .GetNotificationRulesApi()
    .CreateSlackRuleAsync("Critical status to Slack", "10s", "${ r._message }", RuleStatusLevel.CRIT, endpoint, org.Id);

Custom mapping of DomainObject to/from InfluxDB

The default mapper uses Column attributes to define how the DomainObject will be mapped to and from the InfluxDB. The our APIs also allow to specify custom mapper. For more information see following example:

using System;
using System.Threading.Tasks;
using InfluxDB.Client;
using InfluxDB.Client.Api.Domain;
using InfluxDB.Client.Core.Flux.Domain;
using InfluxDB.Client.Writes;

namespace Examples
{
    public static class CustomDomainMapping
    {
        /// <summary>
        /// Define Domain Object
        /// </summary>
        private class Sensor
        {
            /// <summary>
            /// Type of sensor.
            /// </summary>
            public String Type { get; set; }
            
            /// <summary>
            /// Version of sensor.
            /// </summary>
            public String Version { get; set; }

            /// <summary>
            /// Measured value.
            /// </summary>
            public double Value { get; set; }

            public DateTimeOffset Timestamp { get; set; }

            public override string ToString()
            {
                return $"{Timestamp:MM/dd/yyyy hh:mm:ss.fff tt} {Type}, {Version} value: {Value}";
            }
        }

        /// <summary>
        /// Define Custom Domain Object Converter
        /// </summary>
        private class DomainEntityConverter : IDomainObjectMapper
        {
            /// <summary>
            /// Convert to DomainObject.
            /// </summary>
            public object ConvertToEntity(FluxRecord fluxRecord, Type type)
            {
                if (type != typeof(Sensor))
                {
                    throw new NotSupportedException($"This converter doesn't supports: {type}");
                }

                var customEntity = new Sensor
                {
                    Type = Convert.ToString(fluxRecord.GetValueByKey("type")),
                    Version = Convert.ToString(fluxRecord.GetValueByKey("version")),
                    Value = Convert.ToDouble(fluxRecord.GetValueByKey("data")),
                    Timestamp = fluxRecord.GetTime().GetValueOrDefault().ToDateTimeUtc(),
                };
                
                return Convert.ChangeType(customEntity, type);
            }
            
            /// <summary>
            /// Convert to DomainObject.
            /// </summary>
            public T ConvertToEntity<T>(FluxRecord fluxRecord)
            {
                return (T)ConvertToEntity(fluxRecord, typeof(T));
            }

            /// <summary>
            /// Convert to Point
            /// </summary>
            public PointData ConvertToPointData<T>(T entity, WritePrecision precision)
            {
                if (!(entity is Sensor sensor))
                {
                    throw new NotSupportedException($"This converter doesn't supports: {entity}");
                }

                var point = PointData
                    .Measurement("sensor")
                    .Tag("type", sensor.Type)
                    .Tag("version", sensor.Version)
                    .Field("data", sensor.Value)
                    .Timestamp(sensor.Timestamp, precision);

                return point;
            }
        }

        public static async Task Main(string[] args)
        {
            const string host = "http://localhost:9999";
            const string token = "my-token";
            const string bucket = "my-bucket";
            const string organization = "my-org";
            var options = new InfluxDBClientOptions(host)
            {
                Token = token,
                Org = organization,
                Bucket = bucket
            };

            var converter = new DomainEntityConverter();
            using var client = new InfluxDBClient(options);

            //
            // Prepare data to write
            //
            var time = new DateTimeOffset(2020, 11, 15, 8, 20, 15,
                new TimeSpan(3, 0, 0));

            var entity1 = new Sensor
            {
                Timestamp = time,
                Type = "temperature",
                Version = "v0.0.2",
                Value = 15
            };
            var entity2 = new Sensor
            {
                Timestamp = time.AddHours(1),
                Type = "temperature",
                Version = "v0.0.2",
                Value = 15
            };
            var entity3 = new Sensor
            {
                Timestamp = time.AddHours(2),
                Type = "humidity",
                Version = "v0.13",
                Value = 74
            };
            var entity4 = new Sensor
            {
                Timestamp = time.AddHours(3),
                Type = "humidity",
                Version = "v0.13",
                Value = 82
            };

            //
            // Write data
            //
            await client.GetWriteApiAsync(converter)
                .WriteMeasurementsAsync(new []{entity1, entity2, entity3, entity4}, WritePrecision.S);

            //
            // Query Data to Domain object
            //
            var queryApi = client.GetQueryApiSync(converter);

            //
            // Select ALL
            //
            var query = $"from(bucket:\"{bucket}\") " +
                        "|> range(start: 0) " +
                        "|> filter(fn: (r) => r[\"_measurement\"] == \"sensor\")" +
                        "|> pivot(rowKey:[\"_time\"], columnKey: [\"_field\"], valueColumn: \"_value\")";
           
            var sensors = queryApi.QuerySync<Sensor>(query);
            //
            // Print result
            //
            sensors.ForEach(it => Console.WriteLine(it.ToString()));
        }
    }
}

Client configuration file

A client can be configured via App.config file.

The following options are supported:

Property name default description
Url - the url to connect to InfluxDB
Org - default destination organization for writes and queries
Bucket - default destination bucket for writes
Token - the token to use for the authorization
LogLevel NONE rest client verbosity level
Timeout 10000 ms The timespan to wait before the HTTP request times out
AllowHttpRedirects false Configure automatically following HTTP 3xx redirects
VerifySsl true Ignore Certificate Validation Errors when false

The Timeout supports ms, s and m as unit. Default is milliseconds.

Configuration example
<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <configSections>
        <section name="influx2" type="InfluxDB.Client.Configurations.Influx2, InfluxDB.Client" />
    </configSections>

    <influx2 url="http://localhost:8086"
             org="my-org"
             bucket="my-bucket"
             token="my-token"
             logLevel="BODY"
             timeout="10s">
    </influx2>
</configuration>

and then:

var client = InfluxDBClientFactory.Create();

Client connection string

A client can be constructed using a connection string that can contain the InfluxDBClientOptions parameters encoded into the URL.

var client = new InfluxDBClient("http://localhost:8086?timeout=5000&logLevel=BASIC");

The following options are supported:

Property name default description
org - default destination organization for writes and queries
bucket - default destination bucket for writes
token - the token to use for the authorization
logLevel NONE rest client verbosity level
timeout 10000 ms The timespan to wait before the HTTP request times out.
allowHttpRedirects false Configure automatically following HTTP 3xx redirects
verifySsl true Ignore Certificate Validation Errors when false

The timeout supports ms, s and m as unit. Default is milliseconds.

Gzip support

InfluxDBClient does not enable gzip compress for http requests by default. If you want to enable gzip to reduce transfer data's size, you can call:

influxDBClient.EnableGzip();

How to use WebProxy

You can configure the client to tunnel requests through an HTTP proxy. The WebProxy could be configured via InfluxDBClientOptions parameter WebProxy:

var options = new InfluxDBClientOptions("http://localhost:8086")
{
    Token = "my-token",
    WebProxy = new WebProxy("http://proxyserver:80/", true)
};

var client = new InfluxDBClient(options);

Redirects configuration

Client automatically doesn't follows HTTP redirects. You can enable redirects by AllowRedirects configuration option:

var options = new InfluxDBClientOptions("http://localhost:8086")
{
    Token = "my-token",
    AllowRedirects = true
};

using var client = new InfluxDBClient(options);

⚠️ Due to a security reason Authorization header is not forwarded when redirect leads to a different domain. You can create custom Authenticator which change this behaviour - see more.

Log HTTP Request and Response

The Requests and Responses can be logged by changing the LogLevel. LogLevel values are None, Basic, Headers, Body. Note that applying the Body LogLevel will disable chunking while streaming and will load the whole response into memory.

client.SetLogLevel(LogLevel.Body)
Check the server status and version

Server availability can be checked using the influxDBClient.PingAsync() endpoint.

Version

The latest package for .NET CLI:

dotnet add package InfluxDB.Client

Or when using with Package Manager:

Install-Package InfluxDB.Client
Product Versions
.NET net5.0 net5.0-windows net6.0 net6.0-android net6.0-ios net6.0-maccatalyst net6.0-macos net6.0-tvos net6.0-windows net7.0 net7.0-android net7.0-ios net7.0-maccatalyst net7.0-macos net7.0-tvos net7.0-windows
.NET Core netcoreapp2.0 netcoreapp2.1 netcoreapp2.2 netcoreapp3.0 netcoreapp3.1
.NET Standard netstandard2.0 netstandard2.1
.NET Framework net461 net462 net463 net47 net471 net472 net48 net481
MonoAndroid monoandroid
MonoMac monomac
MonoTouch monotouch
Tizen tizen40 tizen60
Xamarin.iOS xamarinios
Xamarin.Mac xamarinmac
Xamarin.TVOS xamarintvos
Xamarin.WatchOS xamarinwatchos
Compatible target framework(s)
Additional computed target framework(s)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (17)

Showing the top 5 NuGet packages that depend on InfluxDB.Client:

Package Downloads
AWF.Fabric

基础结构模块,提供框架级支持

InfluxDB.Client.Linq

The library supports querying InfluxDB 2.x by LINQ expressions.

NBomber.Sinks.InfluxDB

NBomber sink that writes stats data to InfluxDB.

Serilog.Sinks.InfluxDB.Syslog

InfluxDB sink for Serilog with .NET standard 2.0 using syslog format for Influx 2.X

IoTSharp.HealthChecks.InfluxDB

HealthChecks.InfluxDB is the health check package for InfluxDB.

GitHub repositories (7)

Showing the top 5 popular GitHub repositories that depend on InfluxDB.Client:

Repository Stars
Xabaril/AspNetCore.Diagnostics.HealthChecks
Enterprise HealthChecks for ASP.NET Core Diagnostics Package
IoTSharp/IoTSharp
IoTSharp is an open-source IoT platform for data collection, processing, visualization, and device management.
ConcreteMC/Alex
A Minecraft client written in C# aimed at compatibility with MC:Java & MC:Bedrock
melanchall/drywetmidi
.NET library to read, write, process MIDI files and to work with MIDI devices
nickbabcock/OhmGraphite
Expose hardware sensor data to Graphite / InfluxDB / Prometheus / Postgres / Timescaledb
Version Downloads Last updated
4.11.0-dev.10059 182 1/26/2023
4.10.0 3,292 1/26/2023
4.10.0-dev.10033 60 1/25/2023
4.10.0-dev.10032 42 1/25/2023
4.10.0-dev.10031 43 1/25/2023
4.10.0-dev.9936 708 12/26/2022
4.10.0-dev.9935 45 12/26/2022
4.10.0-dev.9881 108 12/21/2022
4.10.0-dev.9880 46 12/21/2022
4.10.0-dev.9818 341 12/16/2022
4.10.0-dev.9773 149 12/12/2022
4.10.0-dev.9756 46 12/12/2022
4.10.0-dev.9693 167 12/6/2022
4.9.0 24,835 12/6/2022
4.9.0-dev.9684 43 12/6/2022
4.9.0-dev.9666 45 12/6/2022
4.9.0-dev.9617 59 12/6/2022
4.9.0-dev.9478 120 12/5/2022
4.9.0-dev.9469 43 12/5/2022
4.9.0-dev.9444 44 12/5/2022
4.9.0-dev.9411 47 12/5/2022
4.9.0-dev.9350 93 12/1/2022
4.8.0 3,070 12/1/2022
4.8.0-dev.9324 114 11/30/2022
4.8.0-dev.9232 97 11/28/2022
4.8.0-dev.9223 46 11/28/2022
4.8.0-dev.9222 45 11/28/2022
4.8.0-dev.9117 318 11/21/2022
4.8.0-dev.9108 46 11/21/2022
4.8.0-dev.9099 46 11/21/2022
4.8.0-dev.9029 125 11/16/2022
4.8.0-dev.8971 50 11/15/2022
4.8.0-dev.8961 49 11/14/2022
4.8.0-dev.8928 47 11/14/2022
4.8.0-dev.8899 46 11/14/2022
4.8.0-dev.8898 48 11/14/2022
4.8.0-dev.8839 50 11/14/2022
4.8.0-dev.8740 182 11/7/2022
4.8.0-dev.8725 46 11/7/2022
4.8.0-dev.8648 223 11/3/2022
4.7.0 30,157 11/3/2022
4.7.0-dev.8625 195 11/2/2022
4.7.0-dev.8594 200 10/31/2022
4.7.0-dev.8579 45 10/31/2022
4.7.0-dev.8557 45 10/31/2022
4.7.0-dev.8540 46 10/31/2022
4.7.0-dev.8518 49 10/31/2022
4.7.0-dev.8517 49 10/31/2022
4.7.0-dev.8509 48 10/31/2022
4.7.0-dev.8377 747 10/26/2022
4.7.0-dev.8360 66 10/25/2022
4.7.0-dev.8350 93 10/24/2022
4.7.0-dev.8335 51 10/24/2022
4.7.0-dev.8334 50 10/24/2022
4.7.0-dev.8223 143 10/19/2022
4.7.0-dev.8178 167 10/17/2022
4.7.0-dev.8170 44 10/17/2022
4.7.0-dev.8148 47 10/17/2022
4.7.0-dev.8133 46 10/17/2022
4.7.0-dev.8097 47 10/17/2022
4.7.0-dev.8034 727 10/11/2022
4.7.0-dev.8025 57 10/11/2022
4.7.0-dev.8009 101 10/10/2022
4.7.0-dev.8001 51 10/10/2022
4.7.0-dev.7959 127 10/4/2022
4.7.0-dev.7905 255 9/30/2022
4.7.0-dev.7875 87 9/29/2022
4.6.0 29,273 9/29/2022
4.6.0-dev.7832 83 9/29/2022
4.6.0-dev.7817 50 9/29/2022
4.6.0-dev.7779 96 9/27/2022
4.6.0-dev.7778 54 9/27/2022
4.6.0-dev.7734 73 9/26/2022
4.6.0-dev.7733 50 9/26/2022
4.6.0-dev.7677 129 9/20/2022
4.6.0-dev.7650 145 9/16/2022
4.6.0-dev.7626 103 9/14/2022
4.6.0-dev.7618 96 9/14/2022
4.6.0-dev.7574 61 9/13/2022
4.6.0-dev.7572 57 9/13/2022
4.6.0-dev.7528 195 9/12/2022
4.6.0-dev.7502 89 9/9/2022
4.6.0-dev.7479 116 9/8/2022
4.6.0-dev.7471 70 9/8/2022
4.6.0-dev.7447 119 9/7/2022
4.6.0-dev.7425 54 9/7/2022
4.6.0-dev.7395 88 9/6/2022
4.6.0-dev.7344 259 8/31/2022
4.6.0-dev.7329 55 8/31/2022
4.6.0-dev.7292 59 8/30/2022
4.6.0-dev.7240 242 8/29/2022
4.5.0 22,626 8/29/2022
4.5.0-dev.7216 77 8/27/2022
4.5.0-dev.7147 238 8/22/2022
4.5.0-dev.7134 265 8/17/2022
4.5.0-dev.7096 113 8/15/2022
4.5.0-dev.7070 167 8/11/2022
4.5.0-dev.7040 118 8/10/2022
4.5.0-dev.7011 167 8/3/2022
4.5.0-dev.6987 70 8/1/2022
4.5.0-dev.6962 82 7/29/2022
4.4.0 22,012 7/29/2022
4.4.0-dev.6901 277 7/25/2022
4.4.0-dev.6843 311 7/19/2022
4.4.0-dev.6804 70 7/19/2022
4.4.0-dev.6789 62 7/19/2022
4.4.0-dev.6760 67 7/19/2022
4.4.0-dev.6705 149 7/14/2022
4.4.0-dev.6663 1,035 6/24/2022
4.4.0-dev.6655 82 6/24/2022
4.3.0 40,149 6/24/2022
4.3.0-dev.multiple.buckets3 218 6/21/2022
4.3.0-dev.multiple.buckets2 197 6/17/2022
4.3.0-dev.multiple.buckets1 64 6/17/2022
4.3.0-dev.6631 75 6/22/2022
4.3.0-dev.6623 71 6/22/2022
4.3.0-dev.6374 385 6/13/2022
4.3.0-dev.6286 911 5/20/2022
4.2.0 32,200 5/20/2022
4.2.0-dev.6257 551 5/13/2022
4.2.0-dev.6248 80 5/12/2022
4.2.0-dev.6233 164 5/12/2022
4.2.0-dev.6194 168 5/10/2022
4.2.0-dev.6193 77 5/10/2022
4.2.0-dev.6158 2,697 5/6/2022
4.2.0-dev.6135 117 5/6/2022
4.2.0-dev.6091 357 4/28/2022
4.2.0-dev.6048 99 4/28/2022
4.2.0-dev.6047 78 4/28/2022
4.2.0-dev.5966 417 4/25/2022
4.2.0-dev.5938 313 4/19/2022
4.1.0 35,929 4/19/2022
4.1.0-dev.5910 286 4/13/2022
4.1.0-dev.5888 81 4/13/2022
4.1.0-dev.5887 79 4/13/2022
4.1.0-dev.5794 780 4/6/2022
4.1.0-dev.5725 309 3/18/2022
4.0.0 40,271 3/18/2022
4.0.0-rc3 769 3/4/2022
4.0.0-rc2 476 2/25/2022
4.0.0-rc1 1,825 2/18/2022
4.0.0-dev.5709 80 3/18/2022
4.0.0-dev.5684 80 3/15/2022
4.0.0-dev.5630 80 3/4/2022
4.0.0-dev.5607 81 3/3/2022
4.0.0-dev.5579 81 2/25/2022
4.0.0-dev.5556 85 2/24/2022
4.0.0-dev.5555 81 2/24/2022
4.0.0-dev.5497 81 2/23/2022
4.0.0-dev.5489 78 2/23/2022
4.0.0-dev.5460 79 2/23/2022
4.0.0-dev.5444 77 2/22/2022
4.0.0-dev.5333 105 2/17/2022
4.0.0-dev.5303 88 2/16/2022
4.0.0-dev.5280 79 2/16/2022
4.0.0-dev.5279 80 2/16/2022
4.0.0-dev.5241 272 2/15/2022
4.0.0-dev.5225 89 2/15/2022
4.0.0-dev.5217 77 2/15/2022
4.0.0-dev.5209 84 2/15/2022
4.0.0-dev.5200 92 2/14/2022
4.0.0-dev.5188 413 2/10/2022
4.0.0-dev.5180 82 2/10/2022
4.0.0-dev.5172 87 2/10/2022
4.0.0-dev.5130 91 2/10/2022
4.0.0-dev.5122 81 2/9/2022
4.0.0-dev.5103 100 2/9/2022
4.0.0-dev.5097 93 2/9/2022
4.0.0-dev.5091 86 2/9/2022
4.0.0-dev.5084 89 2/8/2022
3.4.0-dev.5263 83 2/15/2022
3.4.0-dev.4986 127 2/7/2022
3.4.0-dev.4968 135 2/4/2022
3.3.0 55,569 2/4/2022
3.3.0-dev.4889 125 2/3/2022
3.3.0-dev.4865 112 2/1/2022
3.3.0-dev.4823 261 1/19/2022
3.3.0-dev.4691 271 1/7/2022
3.3.0-dev.4557 2,075 11/26/2021
3.2.0 71,540 11/26/2021
3.2.0-dev.4533 5,007 11/24/2021
3.2.0-dev.4484 303 11/11/2021
3.2.0-dev.4475 150 11/10/2021
3.2.0-dev.4387 214 10/26/2021
3.2.0-dev.4363 178 10/22/2021
3.2.0-dev.4356 137 10/22/2021
3.1.0 37,504 10/22/2021
3.1.0-dev.4303 341 10/18/2021
3.1.0-dev.4293 148 10/15/2021
3.1.0-dev.4286 132 10/15/2021
3.1.0-dev.4240 194 10/12/2021
3.1.0-dev.4202 143 10/11/2021
3.1.0-dev.4183 157 10/11/2021
3.1.0-dev.4131 132 10/8/2021
3.1.0-dev.3999 144 10/5/2021
3.1.0-dev.3841 263 9/29/2021
3.1.0-dev.3798 345 9/17/2021
3.0.0 51,524 9/17/2021
3.0.0-dev.3726 2,455 8/31/2021
3.0.0-dev.3719 106 8/31/2021
3.0.0-dev.3671 293 8/20/2021
2.2.0-dev.3652 115 8/20/2021
2.1.0 94,617 8/20/2021
2.1.0-dev.3605 156 8/17/2021
2.1.0-dev.3584 473 8/16/2021
2.1.0-dev.3558 118 8/16/2021
2.1.0-dev.3527 262 7/29/2021
2.1.0-dev.3519 175 7/29/2021
2.1.0-dev.3490 248 7/20/2021
2.1.0-dev.3445 205 7/12/2021
2.1.0-dev.3434 189 7/9/2021
2.0.0 50,718 7/9/2021
2.0.0-dev.3401 4,235 6/25/2021
2.0.0-dev.3368 192 6/23/2021
2.0.0-dev.3361 164 6/23/2021
2.0.0-dev.3330 201 6/17/2021
2.0.0-dev.3291 188 6/16/2021
1.20.0-dev.3218 460 6/4/2021
1.19.0 64,481 6/4/2021
1.19.0-dev.3204 151 6/3/2021
1.19.0-dev.3160 146 6/2/2021
1.19.0-dev.3159 127 6/2/2021
1.19.0-dev.3084 2,436 5/7/2021
1.19.0-dev.3051 189 5/5/2021
1.19.0-dev.3044 140 5/5/2021
1.19.0-dev.3008 190 4/30/2021
1.18.0 32,107 4/30/2021
1.18.0-dev.2973 170 4/27/2021
1.18.0-dev.2930 1,136 4/16/2021
1.18.0-dev.2919 173 4/13/2021
1.18.0-dev.2893 172 4/12/2021
1.18.0-dev.2880 140 4/12/2021
1.18.0-dev.2856 171 4/7/2021
1.18.0-dev.2830 1,766 4/1/2021
1.18.0-dev.2816 137 4/1/2021
1.17.0 40,192 4/1/2021
1.17.0-dev.linq.17 721 3/18/2021
1.17.0-dev.linq.16 184 3/16/2021
1.17.0-dev.linq.15 169 3/15/2021
1.17.0-dev.linq.14 165 3/12/2021
1.17.0-dev.linq.13 211 3/11/2021
1.17.0-dev.linq.12 149 3/10/2021
1.17.0-dev.linq.11 178 3/8/2021
1.17.0-dev.2776 161 3/26/2021
1.17.0-dev.2713 132 3/25/2021
1.17.0-dev.2707 130 3/25/2021
1.17.0-dev.2652 183 3/19/2021
1.17.0-dev.2619 133 3/18/2021
1.17.0-dev.2566 126 3/16/2021
1.17.0-dev.2549 132 3/15/2021
1.17.0-dev.2505 171 3/12/2021
1.17.0-dev.2446 161 3/11/2021
1.17.0-dev.2402 151 3/8/2021
1.17.0-dev.2371 149 3/5/2021
1.16.0 16,275 3/5/2021
1.16.0-dev.linq.10 1,576 2/4/2021
1.16.0-dev.linq.9 158 2/4/2021
1.16.0-dev.2359 178 3/4/2021
1.16.0-dev.2273 141 2/12/2021
1.16.0-dev.2255 150 2/11/2021
1.16.0-dev.2228 154 2/5/2021
1.16.0-dev.2147 180 1/29/2021
1.15.0 23,971 1/29/2021
1.15.0-dev.linq.8 158 1/28/2021
1.15.0-dev.linq.7 153 1/27/2021
1.15.0-dev.linq.6 214 1/20/2021
1.15.0-dev.linq.5 194 1/19/2021
1.15.0-dev.linq.4 328 1/15/2021
1.15.0-dev.linq.3 136 1/14/2021
1.15.0-dev.linq.2 148 1/13/2021
1.15.0-dev.linq.1 168 1/12/2021
1.15.0-dev.2135 141 1/28/2021
1.15.0-dev.2009 152 1/19/2021
1.15.0-dev.1793 154 1/11/2021
1.15.0-dev.1753 196 1/7/2021
1.15.0-dev.1752 188 1/7/2021
1.15.0-dev.1705 811 12/16/2020
1.15.0-dev.1677 499 12/4/2020
1.14.0 39,385 12/4/2020
1.14.0-dev.1665 199 12/3/2020
1.14.0-dev.1648 195 12/2/2020
1.14.0-dev.1632 238 11/27/2020
1.14.0-dev.1577 392 10/30/2020
1.14.0-dev.1571 257 10/30/2020
1.13.0 13,356 10/30/2020
1.13.0-dev.1545 327 10/15/2020
1.13.0-dev.1516 415 10/8/2020
1.13.0-dev.1489 516 10/2/2020
1.13.0-dev.1478 250 10/2/2020
1.12.0 16,924 10/2/2020
1.12.0-dev.1466 189 10/1/2020
1.12.0-dev.1421 488 9/23/2020
1.12.0-dev.1345 254 9/18/2020
1.12.0-dev.1306 256 9/15/2020
1.12.0-dev.1251 284 9/2/2020
1.12.0-dev.1216 1,880 8/14/2020
1.11.0 23,044 8/14/2020
1.11.0-dev.1205 235 8/14/2020
1.11.0-dev.1185 232 8/10/2020
1.11.0-dev.1166 275 7/28/2020
1.11.0-dev.1150 239 7/28/2020
1.11.0-dev.1144 244 7/28/2020
1.11.0-dev.1125 222 7/20/2020
1.11.0-dev.1111 231 7/17/2020
1.10.0 16,099 7/17/2020
1.10.0-dev.1098 202 7/15/2020
1.10.0-dev.1077 325 7/10/2020
1.10.0-dev.1049 338 6/29/2020
1.10.0-dev.1022 241 6/23/2020
1.10.0-dev.1021 235 6/23/2020
1.10.0-dev.990 243 6/19/2020
1.9.0 13,078 6/19/2020
1.9.0-dev.984 263 6/19/2020
1.9.0-dev.971 215 6/17/2020
1.9.0-dev.955 225 6/17/2020
1.9.0-dev.886 231 6/10/2020
1.9.0-dev.848 254 6/8/2020
1.9.0-dev.842 212 6/8/2020
1.9.0-dev.836 219 6/8/2020
1.9.0-dev.786 1,182 5/27/2020
1.9.0-dev.762 522 5/15/2020
1.8.0 14,834 5/15/2020
1.8.0-dev.748 234 5/12/2020
1.8.0-dev.669 486 4/22/2020
1.8.0-dev.668 218 4/21/2020
1.8.0-dev.661 218 4/20/2020
1.8.0-dev.650 218 4/20/2020
1.8.0-dev.639 223 4/20/2020
1.8.0-dev.620 226 4/17/2020
1.7.0 11,780 4/17/2020
1.7.0-dev.608 241 4/16/2020
1.7.0-dev.574 221 4/14/2020
1.7.0-dev.563 222 4/14/2020
1.7.0-dev.534 233 4/6/2020
1.7.0-dev.528 235 4/6/2020
1.7.0-dev.512 263 4/3/2020
1.7.0-dev.495 234 3/30/2020
1.7.0-dev.469 1,084 3/13/2020
1.6.0 2,728 3/13/2020
1.6.0-dev.458 256 3/13/2020
1.6.0-dev.443 250 3/9/2020
1.6.0-dev.422 271 2/28/2020
1.6.0-dev.410 257 2/27/2020
1.6.0-dev.404 254 2/27/2020
1.6.0-dev.356 257 2/14/2020
1.5.0 1,164 2/14/2020
1.5.0-dev.349 240 2/14/2020
1.5.0-dev.341 245 2/12/2020
1.5.0-dev.312 259 1/22/2020
1.4.0 3,433 1/17/2020
1.3.0 1,818 12/6/2019
1.2.0 5,938 11/8/2019
1.1.0 690 10/11/2019
1.0.0 1,084 8/23/2019