agrolog-sdk
v1.0.1
Published
TypeScript SDK for the Agrolog IoT ThingsBoard API - authentication, asset discovery, and telemetry fetching
Maintainers
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-sdkRequires 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:
configobject passed to the constructor- Environment variables:
AGROLOG_USERNAME,AGROLOG_PASSWORD,AGROLOG_THINGSBOARD_URL .envfile 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
baseUrluses HTTPS. If your ThingsBoard instance uses plain HTTP, setbaseUrlexplicitly. 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.ioAPI 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 | nullclient.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 sensorclient.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 // hPaclient.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 timestampclient.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
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.
