fix: use correct Pulse API endpoints (/all-devices, data-range)
Some checks are pending
Test / backend-test (push) Waiting to run
Test / frontend-test (push) Waiting to run

This commit is contained in:
fullsizemalt 2026-01-05 22:11:42 -08:00
parent 893244169d
commit fb5dba5019

View file

@ -21,8 +21,8 @@ export interface PulseReading {
humidity: number; // % humidity: number; // %
vpd: number; // kPa vpd: number; // kPa
dewpoint: number; // Fahrenheit dewpoint: number; // Fahrenheit
light?: number; // PPFD (for Pro devices) light?: number; // lux
co2?: number; // ppm (for Pro devices) co2?: number; // ppm
timestamp: Date; timestamp: Date;
} }
@ -49,41 +49,73 @@ export class PulseService {
return res.json(); return res.json();
} }
/**
* Get all devices with their latest readings
* Uses /all-devices which returns deviceViewDtos with mostRecentDataPoint
*/
async getAllDevicesWithReadings(): Promise<{ devices: PulseDevice[]; readings: PulseReading[] }> {
const data = await this.fetch('/all-devices');
const devices: PulseDevice[] = [];
const readings: PulseReading[] = [];
// Process device view DTOs
for (const d of (data.deviceViewDtos || [])) {
const deviceId = String(d.id || d.deviceId);
const deviceName = d.name || 'Unknown';
devices.push({
id: deviceId,
name: deviceName,
type: this.getDeviceType(d.deviceType),
isOnline: d.mostRecentDataPoint?.pluggedIn ?? true,
});
if (d.mostRecentDataPoint) {
const dp = d.mostRecentDataPoint;
readings.push({
deviceId,
deviceName,
temperature: dp.temperatureF ?? 0,
humidity: dp.humidityRh ?? 0,
vpd: dp.vpd ?? 0,
dewpoint: dp.dpF ?? this.calculateDewpoint(dp.temperatureF, dp.humidityRh),
light: dp.lightLux || undefined,
co2: dp.co2 || undefined,
timestamp: new Date(dp.createdAt || Date.now()),
});
}
}
// Process universal sensor views if present
for (const s of (data.universalSensorViews || [])) {
const deviceId = String(s.id || s.deviceId);
const deviceName = s.name || 'Unknown Sensor';
devices.push({
id: deviceId,
name: deviceName,
type: 'universal',
isOnline: true,
});
}
return { devices, readings };
}
/** /**
* Get all devices for this grow * Get all devices for this grow
*/ */
async getDevices(): Promise<PulseDevice[]> { async getDevices(): Promise<PulseDevice[]> {
const data = await this.fetch('/devices'); const { devices } = await this.getAllDevicesWithReadings();
return (data.devices || data || []).map((d: any) => ({ return devices;
id: d.id || d.deviceId,
name: d.name || d.deviceName || 'Unknown',
type: d.type || 'pulse',
isOnline: d.isOnline ?? d.online ?? true,
}));
} }
/** /**
* Get current readings for all devices * Get current readings for all devices
*/ */
async getCurrentReadings(): Promise<PulseReading[]> { async getCurrentReadings(): Promise<PulseReading[]> {
const devices = await this.getDevices(); const { readings } = await this.getAllDevicesWithReadings();
const readings: PulseReading[] = [];
for (const device of devices) {
try {
const reading = await this.getDeviceReading(device.id);
if (reading) {
readings.push({
deviceId: device.id,
deviceName: device.name,
...reading,
});
}
} catch (error) {
console.warn(`Failed to get reading for ${device.name}:`, error);
}
}
return readings; return readings;
} }
@ -92,16 +124,16 @@ export class PulseService {
*/ */
async getDeviceReading(deviceId: string): Promise<Omit<PulseReading, 'deviceId' | 'deviceName'> | null> { async getDeviceReading(deviceId: string): Promise<Omit<PulseReading, 'deviceId' | 'deviceName'> | null> {
try { try {
const data = await this.fetch(`/devices/${deviceId}/sensors/current`); const data = await this.fetch(`/devices/${deviceId}/recent-data`);
return { return {
temperature: data.temperature?.value ?? data.temp ?? 0, temperature: data.temperatureF ?? 0,
humidity: data.humidity?.value ?? data.rh ?? 0, humidity: data.humidityRh ?? 0,
vpd: data.vpd?.value ?? data.vpd ?? 0, vpd: data.vpd ?? 0,
dewpoint: data.dewpoint?.value ?? data.dew ?? 0, dewpoint: data.dpF ?? this.calculateDewpoint(data.temperatureF, data.humidityRh),
light: data.ppfd?.value ?? data.light, light: data.lightLux || undefined,
co2: data.co2?.value ?? data.co2, co2: data.co2 || undefined,
timestamp: new Date(data.timestamp || Date.now()), timestamp: new Date(data.createdAt || Date.now()),
}; };
} catch { } catch {
return null; return null;
@ -113,18 +145,18 @@ export class PulseService {
*/ */
async getHistory(deviceId: string, hours: number = 24): Promise<PulseReading[]> { async getHistory(deviceId: string, hours: number = 24): Promise<PulseReading[]> {
const start = new Date(Date.now() - hours * 60 * 60 * 1000).toISOString(); const start = new Date(Date.now() - hours * 60 * 60 * 1000).toISOString();
const data = await this.fetch(`/devices/${deviceId}/sensors/data?start=${start}`); const data = await this.fetch(`/devices/${deviceId}/data-range?start=${start}`);
return (data.readings || data || []).map((r: any) => ({ return (data || []).map((r: any) => ({
deviceId, deviceId,
deviceName: '', deviceName: '',
temperature: r.temperature ?? r.temp ?? 0, temperature: r.temperatureF ?? 0,
humidity: r.humidity ?? r.rh ?? 0, humidity: r.humidityRh ?? 0,
vpd: r.vpd ?? 0, vpd: r.vpd ?? 0,
dewpoint: r.dewpoint ?? r.dew ?? 0, dewpoint: r.dpF ?? this.calculateDewpoint(r.temperatureF, r.humidityRh),
light: r.ppfd ?? r.light, light: r.lightLux || undefined,
co2: r.co2, co2: r.co2 || undefined,
timestamp: new Date(r.timestamp), timestamp: new Date(r.createdAt),
})); }));
} }
@ -133,12 +165,37 @@ export class PulseService {
*/ */
async testConnection(): Promise<{ success: boolean; deviceCount: number; error?: string }> { async testConnection(): Promise<{ success: boolean; deviceCount: number; error?: string }> {
try { try {
const devices = await this.getDevices(); const { devices } = await this.getAllDevicesWithReadings();
return { success: true, deviceCount: devices.length }; return { success: true, deviceCount: devices.length };
} catch (error: any) { } catch (error: any) {
return { success: false, deviceCount: 0, error: error.message }; return { success: false, deviceCount: 0, error: error.message };
} }
} }
/**
* Map device type number to string
*/
private getDeviceType(type: number): string {
const types: Record<number, string> = {
0: 'Pulse One',
1: 'Pulse Pro',
2: 'Pulse Hub',
};
return types[type] || 'Pulse';
}
/**
* Calculate dewpoint from temperature and humidity
*/
private calculateDewpoint(tempF: number, humidity: number): number {
if (!tempF || !humidity) return 0;
const tempC = (tempF - 32) * 5 / 9;
const a = 17.27;
const b = 237.7;
const alpha = ((a * tempC) / (b + tempC)) + Math.log(humidity / 100);
const dewpointC = (b * alpha) / (a - alpha);
return (dewpointC * 9 / 5) + 32;
}
} }
// Singleton instance (initialized with API key from env) // Singleton instance (initialized with API key from env)