File size: 4,062 Bytes
fe9b24f
 
 
 
 
 
800d973
 
 
 
 
1329aed
 
800d973
fe9b24f
800d973
 
 
 
fe9b24f
800d973
 
 
 
 
 
 
 
2dca8eb
 
800d973
 
2dca8eb
 
 
800d973
 
 
 
2dca8eb
800d973
 
 
2dca8eb
 
 
 
 
800d973
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1329aed
 
 
 
 
 
 
 
800d973
 
 
 
 
 
 
 
 
 
 
1329aed
 
 
 
 
 
 
800d973
2dca8eb
 
 
 
800d973
 
 
2dca8eb
800d973
 
 
1329aed
 
 
2dca8eb
 
1329aed
 
 
 
 
 
 
 
 
 
2dca8eb
800d973
 
 
 
 
 
 
2dca8eb
 
800d973
 
 
 
 
 
 
fe9b24f
800d973
 
 
fe9b24f
 
 
 
800d973
2dca8eb
800d973
2dca8eb
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
const express = require("express");
const http = require("http");

const app = express();
const server = http.createServer(app);

// Store LED states
let ledStates = {
  led1: false,
  led2: false,
  led3: false,
  led4: false,
  chase: false
};

// Store SSE clients (ESP8266 devices)
let sseClients = [];

app.use(express.json());
app.use(express.static("."));

// SSE endpoint for ESP8266
app.get("/events", (req, res) => {
  res.setHeader("Content-Type", "text/event-stream");
  res.setHeader("Cache-Control", "no-cache");
  res.setHeader("Connection", "keep-alive");
  res.setHeader("Access-Control-Allow-Origin", "*");
  
  console.log("New SSE client connected");
  
  // Send current state immediately
  Object.keys(ledStates).forEach(led => {
    const message = `data: ${led}:${ledStates[led] ? 1 : 0}\n\n`;
    res.write(message);
    console.log(`Sent initial state: ${led}:${ledStates[led] ? 1 : 0}`);
  });
  
  // Add client to list
  sseClients.push(res);
  console.log(`Total ESP8266 clients: ${sseClients.length}`);
  
  // Send keepalive every 30 seconds
  const keepaliveInterval = setInterval(() => {
    try {
      res.write(`:keepalive\n\n`);
    } catch (err) {
      clearInterval(keepaliveInterval);
    }
  }, 30000);
  
  // Remove client on disconnect
  req.on("close", () => {
    clearInterval(keepaliveInterval);
    sseClients = sseClients.filter(client => client !== res);
    console.log(`ESP8266 disconnected. Total clients: ${sseClients.length}`);
  });
});

// Get current LED states (for frontend)
app.get("/api/leds", (req, res) => {
  res.json(ledStates);
});

// Get connection status
app.get("/api/status", (req, res) => {
  res.json({ 
    connected: sseClients.length > 0,
    clientCount: sseClients.length 
  });
});

// Toggle individual LED (for frontend)
app.post("/api/led/:ledName", (req, res) => {
  const { ledName } = req.params;
  const { state } = req.body;
  
  if (!ledStates.hasOwnProperty(ledName)) {
    return res.status(400).json({ error: "Invalid LED name" });
  }
  
  ledStates[ledName] = state;
  
  // If turning on chase, turn off individual LEDs 1-3
  if (ledName === "chase" && state) {
    ledStates.led1 = false;
    ledStates.led2 = false;
    ledStates.led3 = false;
  }
  
  // Broadcast to all ESP8266 devices via SSE
  const message = `data: ${ledName}:${state ? 1 : 0}\n\n`;
  console.log(`Broadcasting: ${ledName}:${state ? 1 : 0} to ${sseClients.length} clients`);
  
  sseClients.forEach((client, index) => {
    try {
      client.write(message);
    } catch (err) {
      console.error(`Error sending to client ${index}:`, err.message);
    }
  });
  
  // If chase was turned on, also send LED1-3 off commands
  if (ledName === "chase" && state) {
    ["led1", "led2", "led3"].forEach(led => {
      const msg = `data: ${led}:0\n\n`;
      console.log(`Broadcasting: ${led}:0`);
      sseClients.forEach(client => {
        try {
          client.write(msg);
        } catch (err) {
          console.error("Error sending to client:", err.message);
        }
      });
    });
  }
  
  console.log(`${ledName} set to ${state ? "ON" : "OFF"}`);
  res.json({ success: true, ledStates });
});

// Turn all LEDs off
app.post("/api/all-off", (req, res) => {
  Object.keys(ledStates).forEach(led => {
    ledStates[led] = false;
    const message = `data: ${led}:0\n\n`;
    console.log(`Broadcasting: ${led}:0`);
    sseClients.forEach(client => {
      try {
        client.write(message);
      } catch (err) {
        console.error("Error sending to client:", err.message);
      }
    });
  });
  
  console.log("All LEDs turned OFF");
  res.json({ success: true, ledStates });
});

const PORT = process.env.PORT || 3000;
const HOST = "0.0.0.0";
server.listen(PORT, HOST, () => {
  console.log(`\n=================================`);
  console.log(`Server running at http://${HOST}:${PORT}`);
  console.log(`SSE endpoint: http://${HOST}:${PORT}/events`);
  console.log(`API endpoint: http://${HOST}:${PORT}/api/leds`);
  console.log(`=================================\n`);
});