heartbeat-poc.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. #!/usr/bin/env python3
  2. """
  3. heartbeat-poc.py — Proof of Concept heartbeat sender
  4. Run on Pi 4 to test end-to-end pipeline:
  5. Pi → API → WebSocket → Dashboard
  6. Usage:
  7. python3 heartbeat-poc.py
  8. Requirements:
  9. pip install requests
  10. """
  11. import os
  12. import sys
  13. import time
  14. import json
  15. import subprocess
  16. import argparse
  17. from datetime import datetime
  18. # ── Config ──────────────────────────────────────────────────────
  19. API_BASE = os.environ.get('API_BASE', 'http://localhost:3001')
  20. DEVICE_ID = os.environ.get('DEVICE_ID', 'DEMO-001') # khớp với seed
  21. API_KEY = os.environ.get('API_KEY', 'dev-api-key-demo-001') # khớp với seed
  22. INTERVAL_SECONDS = int(os.environ.get('INTERVAL_SECONDS', 30))
  23. def get_device_info() -> dict:
  24. """Gather device telemetry — stub cho POC."""
  25. try:
  26. # Nhiệt độ CPU (Linux)
  27. with open('/sys/class/thermal/thermal_zone0/temp') as f:
  28. temp_milli = int(f.read().strip())
  29. temp_c = round(temp_milli / 1000, 1)
  30. except Exception:
  31. temp_c = None
  32. try:
  33. # Battery (nếu có)
  34. result = subprocess.run(
  35. ['cat', '/sys/class/power_supply/BAT0/capacity'],
  36. capture_output=True, text=True
  37. )
  38. battery = int(result.stdout.strip()) if result.returncode == 0 else None
  39. except Exception:
  40. battery = None
  41. try:
  42. # Disk free (GB)
  43. result = subprocess.run(
  44. ['df', '-BG', '--output=avail', '/'],
  45. capture_output=True, text=True
  46. )
  47. lines = result.stdout.strip().split('\n')
  48. if len(lines) >= 2:
  49. free_str = lines[1].strip().rstrip('G')
  50. storage_free = int(free_str)
  51. else:
  52. storage_free = 50
  53. except Exception:
  54. storage_free = 50
  55. try:
  56. # Network status
  57. result = subprocess.run(
  58. ['ping', '-c1', '-W1', '8.8.8.8'],
  59. capture_output=True
  60. )
  61. network_status = 'online' if result.returncode == 0 else 'degraded'
  62. except Exception:
  63. network_status = 'online'
  64. return {
  65. 'tempC': temp_c,
  66. 'batteryPct': battery,
  67. 'storageFreeGb': storage_free,
  68. 'networkStatus': network_status,
  69. 'lastCaptureAt': datetime.utcnow().isoformat(),
  70. 'capturesToday': 0,
  71. }
  72. def send_heartbeat(device_id: str, api_key: str) -> bool:
  73. """Gửi heartbeat lên API."""
  74. import requests
  75. payload = {
  76. 'deviceId': device_id,
  77. 'apiKey': api_key,
  78. 'status': 'online',
  79. 'firmwareVersion': 'poc-1.0.0',
  80. **get_device_info(),
  81. }
  82. url = f'{API_BASE}/v1/devices/{device_id}/heartbeat'
  83. try:
  84. resp = requests.post(url, json=payload, timeout=10)
  85. if resp.ok:
  86. data = resp.json()
  87. print(f"[{datetime.now().strftime('%H:%M:%S')}] "
  88. f"✓ heartbeat sent | pending commands: {data.get('pendingCommands', 0)}")
  89. return True
  90. else:
  91. print(f"[{datetime.now().strftime('%H:%M:%S')}] "
  92. f"✗ API error {resp.status_code}: {resp.text[:100]}")
  93. return False
  94. except Exception as e:
  95. print(f"[{datetime.now().strftime('%H:%M:%S')}] ✗ connection error: {e}")
  96. return False
  97. def main():
  98. parser = argparse.ArgumentParser(description='POC Heartbeat Sender')
  99. parser.add_argument('--device-id', default=DEVICE_ID, help='Device ID')
  100. parser.add_argument('--api-key', default=API_KEY, help='API Key')
  101. parser.add_argument('--interval', type=int, default=INTERVAL_SECONDS, help='Seconds between heartbeats')
  102. parser.add_argument('--once', action='store_true', help='Send once and exit')
  103. args = parser.parse_args()
  104. print(f'=== Heartbeat POC ===')
  105. print(f' Device: {args.device_id}')
  106. print(f' API: {API_BASE}')
  107. print(f' Interval: {args.interval}s')
  108. print(f' Dashboard: http://localhost:3000')
  109. print()
  110. if args.once:
  111. send_heartbeat(args.device_id, args.api_key)
  112. return
  113. print('Press Ctrl+C to stop.\n')
  114. while True:
  115. send_heartbeat(args.device_id, args.api_key)
  116. time.sleep(args.interval)
  117. if __name__ == '__main__':
  118. main()