npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

agrolog-sdk

v1.0.1

Published

TypeScript SDK for the Agrolog IoT ThingsBoard API - authentication, asset discovery, and telemetry fetching

Readme

agrolog-sdk

TypeScript SDK for the Agrolog IoT ThingsBoard API. Provides typed, authenticated access to silo telemetry, sensor readings, weather stations, aeration systems, and alarms.

For official Agrolog platform documentation, see the Agrolog Docs.

Installation

npm install agrolog-sdk

Requires Node.js >= 18. This package is ESM-only — use import, not require().

Quick Start

import { AgrologClient } from 'agrolog-sdk';

const client = new AgrologClient({
  username: '[email protected]',
  password: 'secret',
  baseUrl: 'https://console.agrolog.io',
});

// Discover topology (must call first)
const topology = await client.connect();
console.log(`Found ${topology.silos.length} silos`);

// Fetch silo telemetry
const telemetry = await client.getSiloTelemetry(topology.silos[0].assetId);
console.log(`Avg temp: ${telemetry.avgTemperature.value}°C`);
console.log(`Avg moisture: ${telemetry.avgMoisture.value}%`);

// Fetch weather
const weather = await client.getWeatherTelemetry();
console.log(`Outdoor temp: ${weather.temperature.value}°C`);

Configuration

Credentials are resolved in priority order:

  1. config object passed to the constructor
  2. Environment variables: AGROLOG_USERNAME, AGROLOG_PASSWORD, AGROLOG_THINGSBOARD_URL
  3. .env file in the working directory
// Explicit config
const client = new AgrologClient({
  username: '[email protected]',
  password: 'secret',
  baseUrl: 'https://console.agrolog.io',
  timeout: 30000,  // ms, default 30s
  logger: (msg) => console.log(msg),  // optional debug logging callback
});

// From environment / .env file
const client = new AgrologClient();

Security note: The default baseUrl uses HTTPS. If your ThingsBoard instance uses plain HTTP, set baseUrl explicitly. Credentials are sent over the network during login — avoid unencrypted connections in production.

| Option | Type | Default | Description | |--------|------|---------|-------------| | username | string | env AGROLOG_USERNAME | ThingsBoard login | | password | string | env AGROLOG_PASSWORD | ThingsBoard password | | baseUrl | string | https://console.agrolog.io | ThingsBoard base URL | | timeout | number | 30000 | HTTP timeout (ms) | | logger | (message: string) => void | — | Debug log callback. Log messages include request paths and retry info but never credentials or tokens. | | debug | boolean | — | Deprecated — will be removed in v2.0.0. When true and no logger set, uses console.log. Use logger: (msg) => console.log(msg) instead. |

.env file example:

[email protected]
AGROLOG_PASSWORD=secret
AGROLOG_THINGSBOARD_URL=https://console.agrolog.io

API Reference

new AgrologClient(config?)

Creates a new client. Throws if username or password cannot be resolved.

client.connect(): Promise<SiteTopology>

Authenticates and discovers the site topology: customer, site, silos, weather station, and aerators. Must be called before any telemetry methods.

const topology = await client.connect();
// topology.customerId  — ThingsBoard customer ID
// topology.siteId      — Site asset ID
// topology.silos       — Array of { assetId, name, type }
// topology.weatherStation — { assetId, name, type } or null
// topology.aerators    — Array of { assetId, name, type }

client.getTopology(): SiteTopology

Returns the topology from the last connect() call. Throws AgrologAPIError (NOT_CONNECTED) if connect() has not been called.

client.isConnected(): boolean

Returns true if connect() has been successfully called.

client.getSiloTelemetry(siloId: string): Promise<SiloTelemetry>

Fetches current aggregate telemetry for a silo asset.

const t = await client.getSiloTelemetry(topology.silos[0].assetId);

// Temperature (°C)
t.minTemperature.value    // number | null
t.avgTemperature.value
t.maxTemperature.value

// Delta temperature (change since last reading)
t.minDeltaTemperature.value
t.avgDeltaTemperature.value
t.maxDeltaTemperature.value

// Moisture (%)
t.minMoisture.value
t.avgMoisture.value
t.maxMoisture.value

// Delta moisture
t.minDeltaMoisture.value
t.avgDeltaMoisture.value
t.maxDeltaMoisture.value

// Each field also has a timestamp (Unix ms or null):
t.avgTemperature.ts   // number | null

client.getSensorLineTelemetry(sensorDeviceId: string): Promise<SensorLineTelemetry>

Fetches per-sensor readings for a temperature/moisture sensor device (sensors 1-3).

const t = await client.getSensorLineTelemetry(deviceId);
t.sensor1Temperature.value   // °C, number | null
t.sensor2Temperature.value
t.sensor3Temperature.value   // null if not available — SDK does NOT compute avg
t.sensor1Moisture.value      // %, number | null
// ...plus delta variants for each sensor

client.getHeadspaceTelemetry(siloId: string): Promise<HeadspaceTelemetry>

Fetches headspace sensor readings for a silo. Automatically discovers the headspace device.

const t = await client.getHeadspaceTelemetry(topology.silos[0].assetId);
t.temperature.value  // °C
t.dewpoint.value     // °C
t.moisture.value     // %
t.co2Level.value     // ppm
t.pressure.value     // hPa

client.getWeatherTelemetry(wsAssetId?: string): Promise<WeatherTelemetry>

Fetches weather station telemetry. Defaults to the weather station discovered by connect().

const weather = await client.getWeatherTelemetry();
weather.temperature.value  // °C
weather.humidity.value     // %

// Or pass an explicit asset ID:
const weather = await client.getWeatherTelemetry('ws-asset-id');

client.getAerationState(aeratorAssetId: string): Promise<AerationState>

Fetches the on/off state of an aeration system.

const aeration = await client.getAerationState(topology.aerators[0].assetId);
aeration.state.value  // string, e.g. 'on' | 'off'
aeration.state.ts     // Unix ms timestamp

client.getAlarms(entityId: string, limit?: number): Promise<Alarm[]>

Fetches active alarms for an entity (silo, aerator, etc.). Default limit: 10, max: 1000.

const alarms = await client.getAlarms(topology.silos[0].assetId, 25);
alarms.forEach(a => {
  console.log(`${a.name} [${a.severity}]: ${a.status}`);
  // a.alarmId, a.type, a.createdTime, a.startTs, a.endTs, a.originatorId, a.details
});

client.discoverSiloDevices(siloId: string): Promise<SiloDevices>

Discovers all devices within a silo (temperature sensors, moisture sensors, headspace sensor, level indicator).

const { siloId, devices } = await client.discoverSiloDevices(topology.silos[0].assetId);
devices.forEach(d => console.log(`${d.name} (${d.type}): ${d.deviceId}`));

client.getAllSiloTelemetry(): Promise<BulkTelemetryResult>

Fetches telemetry for all silos in parallel. Uses Promise.allSettled — partial results are returned if some silos fail. Throws only if all silos fail.

const bulk = await client.getAllSiloTelemetry();

// Successful results
for (const [siloId, telemetry] of bulk.results) {
  console.log(`${siloId}: ${telemetry.avgTemperature.value}°C`);
}

// Check for partial failures
if (bulk.errors.size > 0) {
  for (const [siloId, error] of bulk.errors) {
    console.warn(`Failed to fetch ${siloId}: ${error.message}`);
  }
}

client.refreshAuth(): Promise<void>

Forces a token refresh. Useful if you receive an auth error outside the automatic retry flow.

try {
  await client.getSiloTelemetry(siloId);
} catch (err) {
  if (err instanceof AgrologAPIError && err.isAuthError()) {
    await client.refreshAuth();
    // Retry the request with the new token
    const telemetry = await client.getSiloTelemetry(siloId);
  }
}

Error Handling

All methods throw AgrologAPIError on failure.

import { AgrologClient, AgrologAPIError } from 'agrolog-sdk';
import { ERROR_CODES } from 'agrolog-sdk/config';

try {
  await client.connect();
  const telemetry = await client.getSiloTelemetry(siloId);
} catch (err) {
  if (err instanceof AgrologAPIError) {
    console.error(`[${err.code}] ${err.message}`);
    // err.httpStatus — HTTP status code (if applicable)
    // err.endpoint   — API endpoint that failed

    if (err.isRetryable()) {
      // Safe to retry: 5xx, TIMEOUT, or NETWORK_ERROR
    }
    if (err.isAuthError()) {
      // Authentication failed: 401, 403, AUTH_FAILED, or TOKEN_EXPIRED
    }
    if (err.code === ERROR_CODES.NOT_CONNECTED) {
      // Forgot to call connect() first
    }
  }
}

Error codes (from agrolog-sdk/config -> ERROR_CODES):

| Code | When thrown | |------|-------------| | NOT_CONNECTED | Data method called before connect() | | AUTH_FAILED | Login failed (bad credentials) | | TOKEN_EXPIRED | Token expired and could not be refreshed (reserved) | | DISCOVERY_FAILED | Site, weather station, or device not found | | REQUEST_FAILED | HTTP request failed after retries | | SERVICE_UNAVAILABLE | Server returned 5xx | | TELEMETRY_FAILED | Telemetry request or parse failed (reserved) | | TIMEOUT | Request timed out | | NETWORK_ERROR | Network-level failure (no response) |

Telemetry Values

All telemetry fields return a TimestampedValue<T>:

interface TimestampedValue<T extends string | number | boolean> {
  value: T | null;  // null if the sensor has no reading
  ts: number | null; // Unix timestamp in milliseconds
}

All temperature values are raw Celsius — no conversion is applied. If sensor3Temperature is null in the API response, the SDK returns null (does not compute an average).

Export Paths

// Primary imports (recommended)
import { AgrologClient, AgrologAPIError } from 'agrolog-sdk';

// All public types
import type {
  AgrologConfig,
  TimestampedValue,
  SiteTopology,
  SiloAsset,
  WeatherStationAsset,
  AeratorAsset,
  SiloDevice,
  SiloDevices,
  SiloTelemetry,
  BulkTelemetryResult,
  SensorLineTelemetry,
  HeadspaceTelemetry,
  WeatherTelemetry,
  AerationState,
  Alarm,
} from 'agrolog-sdk/types';

// Constants and error codes
import { ERROR_CODES, DEFAULT_BASE_URL, DEFAULT_TIMEOUT } from 'agrolog-sdk/config';

// Error class from dedicated path
import { AgrologAPIError } from 'agrolog-sdk/errors';

License

MIT

Disclaimer

This is an independent, community-maintained project and is not affiliated with, endorsed by, or officially connected to Agrolog or SAGROline in any way. All product names, logos, and brands are property of their respective owners.