Check speedtest health
Inspect the latest speedtest average published for a gateway via GET /v0/gateways/{address}. The script evaluates validity codes, reward multiplier, and the age of the measurement.
Prerequisites
- Node.js 18 or newer.
- An API key exported as
HELIUMGEEK_API_KEY. - A gateway address provided via
GATEWAY_ADDRESSor the first CLI argument.
Run the example
-
Download mobile-speedtest-check.js and save it locally.
-
Export the environment variables and run:
export HELIUMGEEK_API_KEY="your-api-key"
export GATEWAY_ADDRESS="your-gateway-address"
node mobile-speedtest-check.jsOr provide the address inline:
HELIUMGEEK_API_KEY="your-api-key" node mobile-speedtest-check.js <gateway-address>
Script
mobile-speedtest-check.js
#!/usr/bin/env node
const BASE_URL = (process.env.HELIUMGEEK_API_BASE_URL ?? 'https://api.heliumgeek.com/v0').replace(/\/$/, '');
const API_KEY = process.env.HELIUMGEEK_API_KEY;
const GATEWAY_ADDRESS =
(process.argv[2] ?? process.env.GATEWAY_ADDRESS ?? '').trim();
if (!API_KEY) {
console.error('Set HELIUMGEEK_API_KEY before running this script.');
process.exit(1);
}
if (!GATEWAY_ADDRESS) {
console.error(
'Provide the gateway address as an argument or set GATEWAY_ADDRESS.'
);
process.exit(1);
}
const MBPS_FACTOR = 8 / 1_000_000;
const byteFormatter = new Intl.NumberFormat('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
});
async function main() {
const response = await fetch(
buildUrl(`/gateways/${encodeURIComponent(GATEWAY_ADDRESS)}`),
{
headers: {
'x-api-key': API_KEY,
},
}
);
if (!response.ok) {
throw new Error(`Request failed: ${response.status} ${response.statusText}`);
}
const gateway = await response.json();
const speedtest = gateway?.recent?.speedtestAverage;
if (!speedtest) {
console.log(
`Gateway ${GATEWAY_ADDRESS} has no recorded speedtest averages.`
);
return;
}
const uploadMbps = speedtest.upload * MBPS_FACTOR;
const downloadMbps = speedtest.download * MBPS_FACTOR;
const latencyMs = speedtest.latency ?? 0;
const issues = [];
if (speedtest.validity !== 0) {
const reason = speedtest.validityString || `code ${speedtest.validity}`;
issues.push(`Speedtest flagged: ${reason}`);
}
if (typeof speedtest.rewardMultiplier === 'number' && speedtest.rewardMultiplier < 1) {
issues.push(`Reward multiplier below 1 (${speedtest.rewardMultiplier})`);
}
const lastTimestamp = speedtest.lastSpeedtestTimestamp ?? speedtest.timestamp;
if (lastTimestamp) {
const ageHours = (Date.now() / 1000 - lastTimestamp) / 3600;
if (ageHours > 24) {
issues.push(`Last speedtest is ${ageHours.toFixed(1)} hours old (expected every 12-hour block)`);
}
} else {
issues.push('Speedtest timestamp missing');
}
console.log(`Speedtest summary for ${GATEWAY_ADDRESS}`);
console.log(` Upload: ${formatMbps(uploadMbps)} Mbps`);
console.log(` Download: ${formatMbps(downloadMbps)} Mbps`);
console.log(` Latency: ${latencyMs} ms`);
console.log(
` Validity: ${speedtest.validityString ?? speedtest.validity} | Reward multiplier: ${speedtest.rewardMultiplier ?? 'n/a'}`
);
if (issues.length === 0) {
console.log('Status: OK — the latest speedtest looks healthy.');
} else {
console.log('Status: Attention required');
for (const issue of issues) {
console.log(` - ${issue}`);
}
}
}
function buildUrl(path, query = {}) {
const url = new URL(path.replace(/^\//, ''), `${BASE_URL}/`);
Object.entries(query).forEach(([key, value]) => {
if (value !== undefined && value !== null && value !== '') {
url.searchParams.set(key, String(value));
}
});
return url.toString();
}
function formatMbps(mbps) {
return byteFormatter.format(mbps);
}
main().catch((error) => {
console.error(error.message);
process.exit(1);
});