Nodejs react-switch to send command to Chirpstack

Blockquote
Sounds like you’re a full modern day dev team then :wink:

hahaha I would like to think that.

here are the updated segments of code that I went through

chirpstack.js the gRPC api service

// ChirpStack gRPC client service
const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');
const path = require('path');

// Configuration for ChirpStack connection
const config = {
  // Update these values based on your ChirpStack setup
  host: process.env.CHIRPSTACK_HOST || '',
  port: process.env.CHIRPSTACK_PORT || '',
  apiToken: process.env.CHIRPSTACK_API_TOKEN || '', // Your API token
  //tlsEnabled: process.env.CHIRPSTACK_TLS_ENABLED === 'true' || false,
  protoDir: path.resolve(__dirname, '../../proto') // Location where you'll store the .proto files
};

// Create credentials based on configuration
const getCredentials = () => {
  if (config.tlsEnabled) {
    // For production, use secure connection
    return grpc.credentials.createSsl();
  } else {
    // For development, you might use insecure connection
    return grpc.credentials.createInsecure();
  }
};


// Load all the proto files you need
const loadProtoDescriptors = () => {
    // List of services you want to use from ChirpStack
    const services = [
      'device',
      'device_profile',
      'gateway',
      'application',
      'tenant',
      'user',
      // Add more services as needed
    ];
    
    const protos = {};
    
    services.forEach(service => {
      const protoPath = path.join(config.protoDir, `${service}.proto`);
      console.log(`Loading proto file: ${protoPath}`);  // Debug logging
      
      try {
          const packageDefinition = protoLoader.loadSync(
              protoPath,  // Use full path
              {
                  keepCase: true,
                  longs: String,
                  enums: String,
                  defaults: true,
                  oneofs: true,
                  includeDirs: [config.protoDir]
              }
          );
          
          protos[service] = grpc.loadPackageDefinition(packageDefinition).api;
          console.log(`Successfully loaded ${service} service`);
      } catch (error) {
          console.error(`Failed to load ${service}.proto:`, error);
          throw error;
      }
  });
  
  return protos;
  };
  
  // Create service clients
  const createClients = (protos) => {
    const clients = {};
    const serverAddress = `${config.host}:${config.port}`;
    const credentials = getCredentials();
    
    // Create a client for each service
    Object.keys(protos).forEach(service => {
      const ServiceClass = protos[service][`${service.charAt(0).toUpperCase() + service.slice(1)}Service`];
      if (ServiceClass) {
        clients[service] = new ServiceClass(serverAddress, credentials);
      }
    });
    
    return clients;
  };
  
  // Helper to handle authentication metadata
  const getAuthMetadata = () => {
    const metadata = new grpc.Metadata();
    if (config.apiToken) {
      metadata.add('authorization', `Bearer ${config.apiToken}`);
    }
    return metadata;
  };
  
  // Initialize all clients
  let protos;
  let clients;
  
  try {
    protos = loadProtoDescriptors();
    console.log('Loaded proto descriptors:', Object.keys(protos));
    clients = createClients(protos);
    console.log('Initialized gRPC clients:', {
      device: !!clients.device,
      application: !!clients.application,
  });
  } catch (error) {
    console.error('Failed to initialize ChirpStack gRPC clients:', error);
  }
  
  // Export functions for interacting with the ChirpStack API
  module.exports = {
    // Device operations
    devices: {
      list: (options = {}) => {
          return new Promise((resolve, reject) => {
              if (!clients.device) {
                  return reject(new Error('Device service not initialized'));
              }
              
              // Create the proper request structure
              const request = {
                  limit: options.limit || 100,
                  offset: options.offset || 0,
                  application_id: options.application_id,
                  search: options.search,
                  order_by: options.order_by,
                  order_by_desc: options.order_by_desc,
                  tags: options.tags,
                  device_profile_id: options.device_profile_id
              };
  
              // Add debug logging
              console.log('Sending ListDevices request:', JSON.stringify(request, null, 2));
  
              clients.device.List(request, getAuthMetadata(), (err, response) => {
                  if (err) {
                      console.error('ListDevices error:', err);
                      return reject(err);
                  }
                  console.log('ListDevices response:', JSON.stringify(response, null, 2));
                  resolve(response);
              });
          });
      },


  
      get: (devEui) => {
        return new Promise((resolve, reject) => {
          if (!clients.device) {
            return reject(new Error('Device service not initialized'));
          }
          
          clients.device.Get({ dev_eui: devEui }, getAuthMetadata(), (err, response) => {
            if (err) return reject(err);
            resolve(response);
          });
        });
      },
  
      create: (deviceData) => {
        return new Promise((resolve, reject) => {
          if (!clients.device) {
            return reject(new Error('Device service not initialized'));
          }
          
          clients.device.Create(deviceData, getAuthMetadata(), (err, response) => {
            if (err) return reject(err);
            resolve(response);
          });
        });
      },
  
      update: (deviceData) => {
        return new Promise((resolve, reject) => {
          if (!clients.device) {
            return reject(new Error('Device service not initialized'));
          }
          
          clients.device.Update(deviceData, getAuthMetadata(), (err, response) => {
            if (err) return reject(err);
            resolve(response);
          });
        });
      },
  
      delete: (devEui) => {
        return new Promise((resolve, reject) => {
          if (!clients.device) {
            return reject(new Error('Device service not initialized'));
          }
          
          clients.device.Delete({ dev_eui: devEui }, getAuthMetadata(), (err, response) => {
            if (err) return reject(err);
            resolve(response);
          });
        });
      },
  
      getActivation: (devEui) => {
          return new Promise((resolve, reject) => {
            if (!clients.device) {
              return reject(new Error('Device service not initialized'));
            }
            
            clients.device.GetActivation({ dev_eui: devEui }, getAuthMetadata(), (err, response) => {
              if (err) return reject(err);
              resolve(response);
            });
          });
        },

        Activation: (activationData) => {
            return new Promise((resolve, reject) => {
              if (!clients.device) {
                return reject(new Error('Device service not initialized'));
              }
              
              clients.device.Activation({ dev_eui: activationData }, getAuthMetadata(), (err, response) => {
                if (err) return reject(err);
                resolve(response);
              });
            });
          },

          deactivate: (devEui) => {
            return new Promise((resolve, reject) => {
              if (!clients.device) {
                return reject(new Error('Device service not initialized'));
              }
              
              clients.device.deactivate({ dev_eui: devEui }, getAuthMetadata(), (err, response) => {
                if (err) return reject(err);
                resolve(response);
              });
            });
          },
    
          getKeys: (devEui) => {
            return new Promise((resolve, reject) => {
              if (!clients.device) {
                return reject(new Error('Device service not initialized'));
              }
              
              clients.device.getKeys({ dev_eui: devEui }, getAuthMetadata(), (err, response) => {
                if (err) return reject(err);
                resolve(response);
              });
            });
          },

          createKeys: (keysData) => {
            return new Promise((resolve, reject) => {
              if (!clients.device) {
                return reject(new Error('Device service not initialized'));
              }
              
              clients.device.createKeys({ dev_eui: keysData }, getAuthMetadata(), (err, response) => {
                if (err) return reject(err);
                resolve(response);
              });
            });
          },

          updateKeys: (keysData) => {
            return new Promise((resolve, reject) => {
              if (!clients.device) {
                return reject(new Error('Device service not initialized'));
              }
              
              clients.device.updateKeys({ dev_eui: keysData }, getAuthMetadata(), (err, response) => {
                if (err) return reject(err);
                resolve(response);
              });
            });
          },

          deleteKeys: (devEui) => {
            return new Promise((resolve, reject) => {
              if (!clients.device) {
                return reject(new Error('Device service not initialized'));
              }
              
              clients.device.deleteKeys({ dev_eui: devEui }, getAuthMetadata(), (err, response) => {
                if (err) return reject(err);
                resolve(response);
              });
            });
          },

          getMetrics: ({ dev_eui, start, end, aggregation }) => {
            return new Promise((resolve, reject) => {
                dev_eui, 
                start, 
                end, 
                aggregation
              if (!clients.device) {
                return reject(new Error('Device service not initialized'));
              }
              
              clients.device.getMetrics({ dev_eui, start, end, aggregation }, getAuthMetadata(), (err, response) => {
                if (err) return reject(err);
                resolve(response);
              });
            });
          },

          getLinkMetrics: ({ dev_eui, start, end, aggregation }) => {
            return new Promise((resolve, reject) => {
                dev_eui, 
                start, 
                end, 
                aggregation
              if (!clients.device) {
                return reject(new Error('Device service not initialized'));
              }
              
              clients.device.getLinkMetrics({ dev_eui, start, end, aggregation }, getAuthMetadata(), (err, response) => {
                if (err) return reject(err);
                resolve(response);
              });
            });
          },

        getQueue: (devEui) => {
          return new Promise((resolve, reject) => {
            if (!clients.device) {
              return reject(new Error('Device service not initialized'));
            }
            
            clients.device.GetQueue({ dev_eui: devEui }, getAuthMetadata(), (err, response) => {
              if (err) return reject(err);
              resolve(response);
            });
          });
        },
    
        Enqueue: (queueItem) => {
          return new Promise((resolve, reject) => {
            if (!clients.device) {
              return reject(new Error('Device service not initialized'));
            }
            
            clients.device.Enqueue({ queue_item: queueItem }, getAuthMetadata(), (err, response) => {
              if (err) return reject(err);
              resolve(response);
            });
          });
        },
    
        flushQueue: (devEui) => {
          return new Promise((resolve, reject) => {
            if (!clients.device) {
              return reject(new Error('Device service not initialized'));
            }
            
            clients.device.FlushQueue({ dev_eui: devEui }, getAuthMetadata(), (err, response) => {
              if (err) return reject(err);
              resolve(response);
            });
          });
        },

        getNextFCntDown: (devEui) => {
            return new Promise((resolve, reject) => {
              if (!clients.device) {
                return reject(new Error('Device service not initialized'));
              }
              
              clients.device.getNextFCntDown({ dev_eui: devEui }, getAuthMetadata(), (err, response) => {
                if (err) return reject(err);
                resolve(response);
              });
            });
          },

          flushDevNonces: (devEui) => {
            return new Promise((resolve, reject) => {
              if (!clients.device) {
                return reject(new Error('Device service not initialized'));
              }
              
              clients.device.flushDevNonces({ dev_eui: devEui }, getAuthMetadata(), (err, response) => {
                if (err) return reject(err);
                resolve(response);
              });
            });
          },
  
        // ... existing exports ...
        getConnectionStatus: () => {
            return {
                connected: true, // Implement actual check
                services: Object.keys(clients)
            };
        },
    
        healthCheck: () => {
            return new Promise((resolve) => {
                if (!clients.device) {
                    return resolve(false);
                }
                // Simple request to check connectivity
                clients.device.List({ limit: 1 }, getAuthMetadata(), (err) => {
                    resolve(!err);
                });
            });
        },
  
  
      },
  
    
    // Add similar methods for other services (gateways, applications, etc.)
    applications: {
      // Application related methods
      list: (request = {}) => {
        return new Promise((resolve, reject) => {
          if (!clients.application) {
            return reject(new Error('Application service not initialized'));
          }
          
          clients.application.List(request, getAuthMetadata(), (err, response) => {
            if (err) return reject(err);
            resolve(response);
          });
        });
      },
  
      enqueueDownlink: {
          // Enqueue a downlink message
          Enqueue: (queueItem) => {
            return new Promise((resolve, reject) => {
              if (!clients.device) {
                return reject(new Error('DeviceQueue service not initialized'));
              }
              
              // Validate required fields
              if (!queueItem.queue_item || 
                  !queueItem.queue_item.dev_eui || 
                  !queueItem.queue_item.f_port || 
                  !queueItem.queue_item.data) {
                return reject(new Error('Missing required downlink fields'));
              }
              
              clients.device.Enqueue(queueItem, getAuthMetadata(), (err, response) => {
                if (err) return reject(err);
                resolve(response);
              });
            });
          },
          
          // List queued items for a device
          list: (devEui) => {
            return new Promise((resolve, reject) => {
              if (!clients.device) {
                return reject(new Error('DeviceQueue service not initialized'));
              }
              
              clients.device.List({ dev_eui: devEui }, getAuthMetadata(), (err, response) => {
                if (err) return reject(err);
                resolve(response);
              });
            });
          },
          
          // Flush queue for a device
          flush: (devEui) => {
            return new Promise((resolve, reject) => {
              if (!clients.device) {
                return reject(new Error('DeviceQueue service not initialized'));
              }
              
              clients.device.Flush({ dev_eui: devEui }, getAuthMetadata(), (err, response) => {
                if (err) return reject(err);
                resolve(response);
              });
            });
          }
        },
  
      // Add more application methods as needed
    },
    
    // Add more service objects as needed
  };

devicecontroller.js

const chirpstack = require('../services/chirpstack');

// Get all devices with pagination
exports.getAllDevices = async (req, res) => {
    try {
        const applicationId = process.env.CHIRPSTACK_USER_APP_ID || '';
        console.log("Using Application ID:", applicationId);

        // Get devices from ChirpStack
        const response = await chirpstack.devices.list({
            limit: 100,
            offset: 0,
            application_id: applicationId
        });

        // Transform the data for your frontend
        const devices = response.result.map(device => ({
            devEui: device.dev_eui,
            name: device.name,
            description: device.description,
            lastSeen: device.last_seen_at ? 
                new Date(device.last_seen_at.seconds * 1000).toISOString() : 'Never',
            batteryLevel: device.device_status?.battery_level,
            isActive: device.device_status?.external_power_source || 
                     (device.device_status?.battery_level > 0),
            deviceProfile: device.device_profile_name,
            signalStrength: device.device_status?.margin,
            tags: device.tags
        }));

        res.json({
            success: true,
            count: response.total_count,
            devices
        });

    } catch (error) {
        console.error('Device controller error:', error);
        res.status(500).json({
            success: false,
            error: 'Failed to fetch devices',
            details: process.env.NODE_ENV === 'development' ? error.message : undefined
        });
    }
};

// Get detailed device information including queue items
exports.getDeviceDetails = async (req, res) => {
    try {
        const { devEui } = req.params;
        
        if (!devEui || !/^[A-F0-9]{16}$/i.test(devEui)) {
            return res.status(400).json({ error: 'Invalid DevEUI format' });
        }

        // Get device info
        const device = await chirpstack.devices.get(devEui);
        
        // Get activation info (if needed)
        const activation = await chirpstack.devices.getActivation(devEui).catch(() => null);
        
        res.json({
            success: true,
            device: {
                ...device,
                activation
            }
        });

    } catch (error) {
        if (error.code === 5) { // NOT_FOUND
            return res.status(404).json({ error: 'Device not found' });
        }
        res.status(500).json({ 
            success: false,
            error: 'Failed to fetch device details' 
        });
    }
};
  

// Get device details with full history
exports.getDeviceById = async (req, res) => {
    try {
        const { devEui } = req.params;
        
        if (!devEui) {
            return res.status(400).json({ error: 'DevEUI is required' });
        }
        
        // Get device info
        const device = await chirpstack.devices.get(devEui);
        
        // Get activation info
        const activation = await chirpstack.devices.getActivation(devEui);
        
        // Get queue items
        const queue = await chirpstack.deviceQueue.list({ dev_eui: devEui });
        
        // Get metrics
        const metrics = await chirpstack.devices.getMetrics({
            dev_eui: devEui,
            start: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000), // Last 7 days
            end: new Date(),
            aggregation: 'DAY' // or 'DAY', 'MONTH'
        });

        res.json({
            success: true,
            result: {
                ...device,
                activation,
                queue: queue.result,
                metrics
            }
        });
    } catch (error) {
        console.error(`Error fetching device ${req.params.devEui}:`, error);
        
        if (error.code === 5) { // NOT_FOUND
            return res.status(404).json({ error: 'Device not found' });
        }
        
        res.status(500).json({ 
            success: false,
            error: error.message || 'Failed to fetch device' 
        });
    }
};

// Create a new device
exports.createDevice = async (req, res) => {
  try {
    const deviceData = req.body;
    
    // Validate required fields
    if (!deviceData.device || !deviceData.device.dev_eui || !deviceData.device.application_id) {
      return res.status(400).json({ error: 'Missing required device information' });
    }
    
    const response = await chirpstack.devices.create(deviceData);
    res.status(201).json(response);
  } catch (error) {
    console.error('Error creating device:', error);
    res.status(500).json({ error: error.message || 'Failed to create device' });
  }
};

// Update an existing device
exports.updateDevice = async (req, res) => {
  try {
    const { devEui } = req.params;
    const deviceData = req.body;
    
    // Ensure the device data contains the DevEUI from the URL
    if (!deviceData.device) {
      deviceData.device = {};
    }
    deviceData.device.dev_eui = devEui;
    
    const response = await chirpstack.devices.update(deviceData);
    res.json(response);
  } catch (error) {
    console.error(`Error updating device ${req.params.devEui}:`, error);
    res.status(500).json({ error: error.message || 'Failed to update device' });
  }
};

// Delete a device
exports.deleteDevice = async (req, res) => {
  try {
    const { devEui } = req.params;
    
    if (!devEui) {
      return res.status(400).json({ error: 'DevEUI is required' });
    }
    
    await chirpstack.devices.delete(devEui);
    res.status(204).send(); // No content response for successful deletion
  } catch (error) {
    console.error(`Error deleting device ${req.params.devEui}:`, error);
    res.status(500).json({ error: error.message || 'Failed to delete device' });
  }
};

// Device metrics
exports.getMetrics = async (req, res) => {
    try {
        const { devEui } = req.params;
        const { start, end, aggregation } = req.query;
        
        const metrics = await chirpstack.devices.getMetrics({
            dev_eui: devEui,
            start: new Date(start),
            end: new Date(end),
            aggregation: aggregation || 'HOUR'
        });
        
        res.json({ 
            success: true,
            metrics 
        });
    } catch (error) {
        console.error(`Error getting metrics for device ${req.params.devEui}:`, error);
        res.status(500).json({ 
            success: false,
            error: error.message || 'Failed to get device metrics' 
        });
    }
};

what i get when I run the test

PS C:\intellisecIoT\nodejs\intelliseciot> node test-script.js

Successfully loaded user service
Loaded proto descriptors: [
  'device',
  'device_profile',
  'gateway',
  'application',
  'tenant',
  'user'
]
Initialized gRPC clients: { device: true, application: true }
Testing device listing...
Sending ListDevices request: {
  "limit": 5,
  "offset": 0,
  "application_id": ""
}
ListDevices response: {
  "result": [
    {
      "tags": {},
      "dev_eui": "24e124445d354713",
      "created_at": {
        "seconds": "1743751598",
        "nanos": 872155000
      },
      "updated_at": {
        "seconds": "1744017014",
        "nanos": 572388000
      },
      "last_seen_at": {
        "seconds": "1747650146",
        "nanos": 759629000
      },
      "name": "IO Controller Test Generator",
      "description": "",
      "device_profile_id": "49729896-cd65-471a-be26-52f0b98f43fc",
      "device_profile_name": "UC300",
      "device_status": {
        "margin": 6,
        "external_power_source": false,
        "battery_level": -1
      }
    },
    {
      "tags": {},
      "dev_eui": "24e124136d319204",
      "created_at": {
        "seconds": "1741768453",
        "nanos": 971927000
      },
      "updated_at": {
        "seconds": "1741769023",
        "nanos": 221781000
      },
      "last_seen_at": {
        "seconds": "1747650035",
        "nanos": 315696000
      },
      "name": "Kitchen Freezer Sensor",
      "description": "",
      "device_profile_id": "bc08b8a3-ce21-48dc-b08d-9dfe5d7f20ae",
      "device_profile_name": "EM300-MCS",
      "device_status": {
        "margin": 1,
        "external_power_source": false,
        "battery_level": 96.8499984741211
      }
    },
    {
      "tags": {},
      "dev_eui": "24e124538c195163",
      "created_at": {
        "seconds": "1741768251",
        "nanos": 648175000
      },
      "updated_at": {
        "seconds": "1741768251",
        "nanos": 648175000
      },
      "last_seen_at": {
        "seconds": "1747649766",
        "nanos": 368865000
      },
      "name": "Kitchen Movement Sensor",
      "description": "",
      "device_profile_id": "20e92c36-842a-4db2-93c5-9e622be64234",
      "device_profile_name": "WS202",
      "device_status": {
        "margin": 6,
        "external_power_source": false,
        "battery_level": 48.810001373291016
      }
    },
    {
      "tags": {},
      "dev_eui": "24e124716e210513",
      "created_at": {
        "seconds": "1741768395",
        "nanos": 558757000
      },
      "updated_at": {
        "seconds": "1741768395",
        "nanos": 558757000
      },
      "last_seen_at": {
        "seconds": "1747649993",
        "nanos": 42397000
      },
      "name": "People Counter",
      "description": "",
      "device_profile_id": "4a16c151-ab3f-4ef1-9f9f-aef05302d69a",
      "device_profile_name": "VS350",
      "device_status": {
        "margin": 7,
        "external_power_source": true,
        "battery_level": -1
      }
    },
    {
      "tags": {},
      "dev_eui": "24e124535c400704",
      "created_at": {
        "seconds": "1741768580",
        "nanos": 944276000
      },
      "updated_at": {
        "seconds": "1741768580",
        "nanos": 944276000
      },
      "last_seen_at": {
        "seconds": "1747649810",
        "nanos": 48745000
      },
      "name": "Test Panic Button",
      "description": "",
      "device_profile_id": "830b2ccc-c563-425c-881c-9b71815dae8e",
      "device_profile_name": "WS101",
      "device_status": {
        "margin": 7,
        "external_power_source": false,
        "battery_level": 87.79000091552734
      }
    }
  ],
  "total_count": 5
}
Devices: {
  "result": [
    {
      "tags": {},
      "dev_eui": "24e124445d354713",
      "created_at": {
        "seconds": "1743751598",
        "nanos": 872155000
      },
      "updated_at": {
        "seconds": "1744017014",
        "nanos": 572388000
      },
      "last_seen_at": {
        "seconds": "1747650146",
        "nanos": 759629000
      },
      "name": "IO Controller Test Generator",
      "description": "",
      "device_profile_id": "49729896-cd65-471a-be26-52f0b98f43fc",
      "device_profile_name": "UC300",
      "device_status": {
        "margin": 6,
        "external_power_source": false,
        "battery_level": -1
      }
    },
    {
      "tags": {},
      "dev_eui": "24e124136d319204",
      "created_at": {
        "seconds": "1741768453",
        "nanos": 971927000
      },
      "updated_at": {
        "seconds": "1741769023",
        "nanos": 221781000
      },
      "last_seen_at": {
        "seconds": "1747650035",
        "nanos": 315696000
      },
      "name": "Kitchen Freezer Sensor",
      "description": "",
      "device_profile_id": "bc08b8a3-ce21-48dc-b08d-9dfe5d7f20ae",
      "device_profile_name": "EM300-MCS",
      "device_status": {
        "margin": 1,
        "external_power_source": false,
        "battery_level": 96.8499984741211
      }
    },
    {
      "tags": {},
      "dev_eui": "24e124538c195163",
      "created_at": {
        "seconds": "1741768251",
        "nanos": 648175000
      },
      "updated_at": {
        "seconds": "1741768251",
        "nanos": 648175000
      },
      "last_seen_at": {
        "seconds": "1747649766",
        "nanos": 368865000
      },
      "name": "Kitchen Movement Sensor",
      "description": "",
      "device_profile_id": "20e92c36-842a-4db2-93c5-9e622be64234",
      "device_profile_name": "WS202",
      "device_status": {
        "margin": 6,
        "external_power_source": false,
        "battery_level": 48.810001373291016
      }
    },
    {
      "tags": {},
      "dev_eui": "24e124716e210513",
      "created_at": {
        "seconds": "1741768395",
        "nanos": 558757000
      },
      "updated_at": {
        "seconds": "1741768395",
        "nanos": 558757000
      },
      "last_seen_at": {
        "seconds": "1747649993",
        "nanos": 42397000
      },
      "name": "People Counter",
      "description": "",
      "device_profile_id": "4a16c151-ab3f-4ef1-9f9f-aef05302d69a",
      "device_profile_name": "VS350",
      "device_status": {
        "margin": 7,
        "external_power_source": true,
        "battery_level": -1
      }
    },
    {
      "tags": {},
      "dev_eui": "24e124535c400704",
      "created_at": {
        "seconds": "1741768580",
        "nanos": 944276000
      },
      "updated_at": {
        "seconds": "1741768580",
        "nanos": 944276000
      },
      "last_seen_at": {
        "seconds": "1747649810",
        "nanos": 48745000
      },
      "name": "Test Panic Button",
      "description": "",
      "device_profile_id": "830b2ccc-c563-425c-881c-9b71815dae8e",
      "device_profile_name": "WS101",
      "device_status": {
        "margin": 7,
        "external_power_source": false,
        "battery_level": 87.79000091552734
      }
    }
  ],
  "total_count": 5
}
Found 5 devices

Testing metrics for device: IO Controller Test Generator (24e124445d354713)
Metrics for IO Controller Test Generator:
State Metrics:
  gpio_out_2: off ()
  gpio_out_1: off ()

Time Series Metrics:

Testing metrics for device: Kitchen Freezer Sensor (24e124136d319204)
Metrics for Kitchen Freezer Sensor:
State Metrics:
  magnet_status: closed ()

Time Series Metrics:
  battery:
    battery: No data points available
  humidity:
    humidity: No data points available
  temperature:
    temperature: No data points available

Testing metrics for device: Kitchen Movement Sensor (24e124538c195163)
Metrics for Kitchen Movement Sensor:
State Metrics:
  pir: movement detected ()
  daylight: light ()

Time Series Metrics:
  battery:
    battery: No data points available

Testing metrics for device: People Counter (24e124716e210513)
Metrics for People Counter:
State Metrics:

Time Series Metrics:

Testing metrics for device: Test Panic Button (24e124535c400704)
Metrics for Test Panic Button:
State Metrics:

Time Series Metrics:

now I am trying to figure out why some of the metrics are not showing values but at lease steps in the right way.

so with the chirpstack’s SQL I would need to setup a purge to keep data for a year anything older than that purge it? will figure that out once i got the data coming through the right way first.

I think the issue is just the way your code handles the inputs of getMetrics. I just tried writing some similar code for getMetrics to test and ran into the same “No data points available” at first, but solved it by changing how I handled the protobuf.

Hard for me to really dig into your implementation though, and frankly I find it messy enough that I don’t want to touch it. But you should be able to gain some insight into how to properly frame the inputs and parse the output from this code segment that works for me:

device.js:

const grpc = require("@grpc/grpc-js");
const device_grpc = require("@chirpstack/chirpstack-api/api/device_grpc_pb");
const device_pb = require("@chirpstack/chirpstack-api/api/device_pb");
const common_pb = require('@chirpstack/chirpstack-api/common/common_pb');
const { Timestamp } = require('google-protobuf/google/protobuf/timestamp_pb');


/**
 * Create and return a gRPC client for the DeviceService.
 * @param {string} server - The gRPC server address.
 * @param {string} apiToken - The API token for authentication.
 * @returns {{client: Object, metadata: Object}} - The gRPC client and metadata.
 */
function createDeviceClient(server, apiToken) {
  const client = new device_grpc.DeviceServiceClient(
    server,
    grpc.credentials.createInsecure(),
  );

  const metadata = new grpc.Metadata();
  metadata.set("authorization", "Bearer " + apiToken);

  return { client, metadata };
}


 async function getDeviceMetrics(client, metadata, devEui, startTime, endTime, aggregation = "DAY") {
   return new Promise((resolve, reject) => {
     const req = new device_pb.GetDeviceMetricsRequest();
     req.setDevEui(devEui);

     const startTimestamp = new Timestamp();
     startTimestamp.fromDate(startTime);
     req.setStart(startTimestamp);

     const endTimestamp = new Timestamp();
     endTimestamp.fromDate(endTime);
     req.setEnd(endTimestamp);

     req.setAggregation(common_pb.Aggregation[aggregation]);

     client.getMetrics(req, metadata, (err, resp) => {
       if (err) return reject(err);
       resolve(resp); 
     });
   });
 }


module.exports = { createDeviceClient, getDeviceMetrics };

devicecontroller.js:

const { createDeviceClient, getDeviceMetrics } = require("./device");

const SERVER = "<address>"; // Replace with your server address
const API_TOKEN = "<api-token>"; // Replace with your API token
const DEV_EUI = "<dev-eui>"; // Replace with your actual DevEUI

(async () => {
  try {
    const { client: deviceClient, metadata } = createDeviceClient(SERVER, API_TOKEN);

    const endTime = new Date();
    const startTime = new Date(endTime.getTime() - 24 * 60 * 60 * 1000); // last 24 hours

    console.log(`\nFetching metrics for device: ${DEV_EUI}\n`);

    const response = await getDeviceMetrics(deviceClient, metadata, DEV_EUI, startTime, endTime, "DAY");

    const metricsMap = response.getMetricsMap();
    const statesMap = response.getStatesMap();

    if (metricsMap.getLength() > 0) {
      console.log("📊 Device Metrics:");
      metricsMap.forEach((metric, name) => {
        console.log(`\n🔸 ${name}:`);
        const timestamps = metric.getTimestampsList();
        const datasets = metric.getDatasetsList();

        if (datasets.length === 0 || timestamps.length === 0) {
          console.log("  No data points available.");
          return;
        }

        datasets.forEach(dataset => {
          const label = dataset.getLabel();
          const values = dataset.getDataList();
          console.log(`  • ${label}:`);
          for (let i = 0; i < values.length; i++) {
            const ts = timestamps[i];
            const date = new Date(ts.getSeconds() * 1000).toISOString().split("T")[0];
            const value = values[i];
            console.log(`    ${date}: ${value}`);
          }
        });
      });
    } else {
      console.log("📊 Device Metrics: None available");
    }

    if (statesMap.getLength() > 0) {
      console.log("\n🟢 Device States:");
      statesMap.forEach((value, key) => {
        console.log(`  • ${key}: ${value}`);
      });
    } else {
      console.log("\n🟢 Device States: None available");
    }
    
  } catch (error) {
    console.error("Error fetching device metrics:", error);
  }
})();

Running node devicecontroller.js directly should output the metrics as expected (given you have configured the metrics for that device in your device profile). I ran it on my temperature / humidity sensor and this was the output:

% node devicecontroller.js

Fetching metrics for device: 7894e80000027a0a

📊 Device Metrics:

🔸 ATH_Humidity:
  • ATH_Humidity:
    2025-05-21: 40.50384521484375
    2025-05-22: 41.755001068115234

🔸 ATH_Temperature:
  • ATH_Temperature:
    2025-05-21: 18.52692222595215
    2025-05-22: 17.559999465942383

🟢 Device States: No state metrics available

If you share that code with your ‘dev team’ they likely will be able to tell the issue with your implementation. Maybe just try running these yourself first though to make sure it works on your machine / you have the proper imports and modules.

Ya you would be responsible for cleaning up the database yourself, or else it will just grow and grow in size, what timeframe or rules you decide are necessary for that are up to you. A bit confused by what you mean by having the data coming in the right way. If you went with the SQL integration all device uplinks would automatically be put into the database, so you would avoid pulling the data through gRPC entirely, and your deviceController would just pull the metrics from your own SQL database rather then by API calls.