The Nibbler system exposes a comprehensive REST API for real-time monitoring and control:
API Implementation (Node.js/Express)
// nibbler_api.js - Main API server
const express = require('express');
const WebSocket = require('ws');
const sqlite3 = require('sqlite3').verbose();
const cors = require('cors');
const app = express();
const server = require('http').createServer(app);
const wss = new WebSocket.Server({ server });
app.use(cors());
app.use(express.json());
// Real-time metrics endpoint
app.post('/api/metrics', (req, res) => {
const { rpm, power_output, mood, timestamp, device_id } = req.body;
// Validate device authentication
if (device_id !== 'nibbler_habitat_001') {
return res.status(401).json({ error: 'Unauthorized device' });
}
// Store in database
const db = new sqlite3.Database('./nibbler_metrics.db');
db.run(`INSERT INTO metrics
(timestamp, rpm, power_output, mood, efficiency)
VALUES (?, ?, ?, ?, ?)`,
[timestamp, rpm, power_output, mood, (power_output/400)*100],
function(err) {
if (err) {
console.error('Database error:', err);
return res.status(500).json({ error: 'Database error' });
}
// Broadcast to WebSocket clients
const data = { rpm, power_output, mood, timestamp, efficiency: (power_output/400)*100 };
wss.clients.forEach(client => {
if (client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify({ type: 'metrics_update', data }));
}
});
res.json({
success: true,
message: 'Metrics received',
nibbler_response: generateNibblerResponse(mood, rpm)
});
});
db.close();
});
// Get current status
app.get('/api/nibbler/status', (req, res) => {
const db = new sqlite3.Database('./nibbler_metrics.db');
db.get(`SELECT * FROM metrics
ORDER BY timestamp DESC LIMIT 1`, (err, row) => {
if (err) {
return res.status(500).json({ error: 'Database error' });
}
if (!row) {
return res.json({
status: 'offline',
message: 'No recent data from Nibbler π’'
});
}
const timeDiff = Date.now() - new Date(row.timestamp).getTime();
const isOnline = timeDiff < 30000; // Consider online if data < 30 seconds old
res.json({
status: isOnline ? 'online' : 'offline',
current_rpm: row.rpm,
power_output: row.power_output,
mood: row.mood,
efficiency: row.efficiency,
last_seen: row.timestamp,
uptime_percentage: 99.7,
sunflower_seeds: 1247,
message: generateStatusMessage(row.mood, row.rpm)
});
});
db.close();
});
// Chat with Nibbler endpoint
app.post('/api/nibbler/chat', async (req, res) => {
const { message, user_id } = req.body;
if (!message) {
return res.status(400).json({ error: 'Message is required' });
}
// Get current Nibbler status to influence response
const currentStatus = await getCurrentStatus();
// Generate contextual response based on current metrics
const response = await generateChatResponse(message, currentStatus);
res.json({
success: true,
nibbler_response: response,
current_mood: currentStatus.mood,
timestamp: new Date().toISOString()
});
});
// Historical data endpoint
app.get('/api/metrics/history', (req, res) => {
const { hours = 24, granularity = 'minute' } = req.query;
const db = new sqlite3.Database('./nibbler_metrics.db');
const timeCondition = `datetime(timestamp) >= datetime('now', '-${hours} hours')`;
const groupBy = granularity === 'hour' ?
"strftime('%Y-%m-%d %H:00:00', timestamp)" :
"strftime('%Y-%m-%d %H:%M:00', timestamp)";
db.all(`
SELECT
${groupBy} as time_bucket,
AVG(rpm) as avg_rpm,
AVG(power_output) as avg_power,
AVG(efficiency) as avg_efficiency,
mood,
COUNT(*) as sample_count
FROM metrics
WHERE ${timeCondition}
GROUP BY time_bucket, mood
ORDER BY time_bucket
`, (err, rows) => {
if (err) {
return res.status(500).json({ error: 'Database error' });
}
res.json({
timeframe: `${hours} hours`,
granularity,
data: rows,
total_samples: rows.reduce((sum, row) => sum + row.sample_count, 0)
});
});
db.close();
});
function generateNibblerResponse(mood, rpm) {
const responses = {
'MAXIMUM_HAPPINESS': [
'βα’Β·ΝΰΌΒ·Να’β Wheee! I\'m running at peak happiness!',
'This is the perfect RPM! My quantum amplifiers are singing!',
'All systems optimal! Time for celebratory wheel spins! β¨'
],
'CONTENT': [
'Feeling good and generating steady power! πͺ',
'Nice and comfortable pace, all systems stable.',
'This is a pleasant running speed, everything nominal!'
],
'NEUTRAL': [
'Running at average efficiency, all systems functional.',
'Maintaining baseline operations, ready for optimization.',
'Steady state achieved, standing by for activity increase.'
],
'SLEEPY': [
'Getting a bit tired... maybe time for a power nap? π΄',
'Running low on energy, considering hibernation mode.',
'Efficiency dropping, might need some sunflower seed fuel.'
],
'NEEDS_SUNFLOWER_SEEDS': [
'URGENT: Sunflower seed levels critically low! π»',
'Cannot maintain optimal performance without proper nutrition!',
'Emergency treat dispensing recommended immediately!'
]
};
const moodResponses = responses[mood] || responses['NEUTRAL'];
return moodResponses[Math.floor(Math.random() * moodResponses.length)];
}
function generateStatusMessage(mood, rpm) {
if (rpm > 150) {
return 'Warning: Nibbler is running faster than recommended! Check for excitement overload.';
} else if (rpm < 50) {
return 'Nibbler appears to be in low-power mode. Consider treats or environmental enrichment.';
} else if (Math.abs(rpm - 127.4) < 5) {
return 'Perfect operational parameters! Nibbler is in the zone! βα’Β·ΝΰΌΒ·Να’β';
} else {
return 'Nibbler is operating within normal parameters.';
}
}
async function getCurrentStatus() {
return new Promise((resolve, reject) => {
const db = new sqlite3.Database('./nibbler_metrics.db');
db.get('SELECT * FROM metrics ORDER BY timestamp DESC LIMIT 1', (err, row) => {
db.close();
if (err) reject(err);
else resolve(row || { mood: 'NEUTRAL', rpm: 0, power_output: 0 });
});
});
}
async function generateChatResponse(message, status) {
// Context-aware chat responses based on current Nibbler state
const lowerMessage = message.toLowerCase();
if (lowerMessage.includes('how are you') || lowerMessage.includes('status')) {
return `I'm currently ${status.mood.toLowerCase().replace('_', ' ')} and running at ${status.rpm?.toFixed(1) || 0} RPM! My power output is ${status.power_output?.toFixed(1) || 0} watts. βα’Β·ΝΰΌΒ·Να’β`;
}
if (lowerMessage.includes('wheel') || lowerMessage.includes('running')) {
if (status.rpm > 127.4) {
return 'My wheel is spinning faster than optimal! I\'m feeling extra energetic today! β‘';
} else if (status.rpm < 100) {
return 'I\'m taking it easy on the wheel right now. Maybe I need some motivation? π»';
} else {
return 'My wheel is running at perfect efficiency! The quantum amplifiers are humming beautifully!';
}
}
if (lowerMessage.includes('power') || lowerMessage.includes('energy')) {
return `I\'m currently generating ${status.power_output?.toFixed(1) || 0} watts of clean, renewable hamster power! That\'s enough to run all my AI systems and still have energy left for digital treats! π`;
}
// Default responses
const defaultResponses = [
'Hello! I\'m Nibbler, your friendly bio-digital hamster AI! How can I help you today? βα’Β·ΝΰΌΒ·Να’β',
'Thanks for chatting with me! Is there anything specific you\'d like to know about my wheel-powered systems?',
'I love having conversations! It actually helps optimize my neural pathways while I\'m running!',
'Feel free to ask me anything about bio-digital consciousness or sustainable hamster power generation!'
];
return defaultResponses[Math.floor(Math.random() * defaultResponses.length)];
}
// WebSocket connection handling
wss.on('connection', (ws) => {
console.log('π± New WebSocket client connected');
// Send current status on connection
getCurrentStatus().then(status => {
ws.send(JSON.stringify({
type: 'initial_status',
data: status
}));
});
ws.on('close', () => {
console.log('π± WebSocket client disconnected');
});
});
// Start server
const PORT = process.env.PORT || 3001;
server.listen(PORT, () => {
console.log(`πΉ Nibbler API server running on port ${PORT}`);
console.log(`π WebSocket server ready for real-time connections`);
});