index.html 8.1 K raw
1
<!doctype html>
2
<html>
3
  <head>
4
    <title>Steve's Pi</title>
5
    <meta charset="utf-8" />
6
    <meta
7
      name="description"
8
      content="A peek into Steve's Raspberry Pi and the services it runs"
9
    />
10
    <meta name="viewport" content="width=device-width, initial-scale=1" />
11
12
    <meta property="og:type" content="website" />
13
    <meta property="og:title" content="Steve's Pi" />
14
    <meta
15
      property="og:description"
16
      content="A peek into Steve's Raspberry Pi and the services it runs"
17
    />
18
    <meta property="og:url" content="https://pi.stevedylan.dev" />
19
    <meta property="og:site_name" content="Steve's Pi" />
20
    <meta property="og:image" content="https://stevedylan.dev/pi.png" />
21
    <meta property="og:image:width" content="1200" />
22
    <meta property="og:image:height" content="630" />
23
24
    <link rel="icon" href="https://stevedylan.dev/favicon.ico" sizes="any" />
25
    <link
26
      rel="icon"
27
      href="https://stevedylan.dev/icon.svg"
28
      type="image/svg+xml"
29
    />
30
    <link rel="apple-touch-icon" href="/apple-touch-icon.png" />
31
    <style>
32
      @font-face {
33
          font-family: 'CommitMono';
34
          src: url('https://stevedylan.dev/CommitMono-400-Regular.otf') format('opentype'),
35
          font-weight: normal;
36
          font-style: normal;
37
      }
38
         html {
39
             padding: 0;
40
             margin: 0 1rem 0 1rem;
41
             box-sizing: border-box;
42
             background: #000000;
43
             color: #FFFFFF;
44
             font-family: 'CommitMono', sans-serif;
45
         }
46
         body {
47
             display: flex;
48
             justify-content: center;
49
             align-items: center;
50
             flex-direction: column;
51
             min-height: 90vh;
52
             max-width: 500px;
53
             margin: auto;
54
             font-size: 14px;
55
         }
56
         .stats-container {
57
         	display: flex;
58
          	flex-direction: column;
59
           	justify-content: flex-start;
60
         }
61
         .ipfs-div {
62
         	display: flex;
63
             justify-content: flex-start;
64
             align-items: center;
65
          	gap: 0.5rem;
66
         }
67
         .source-link {
68
             padding-top: 2rem;
69
         }
70
         .link-container {
71
             display: flex;
72
             justify-content: flex-start;
73
             align-items: center;
74
             gap: 1rem;
75
         }
76
         .truncate {
77
             display: inline-block;
78
             max-width: calc(100% - 30px); /* Adjust based on the width of "ID: " */
79
             white-space: nowrap;
80
             overflow: hidden;
81
             text-overflow: ellipsis;
82
             vertical-align: bottom;
83
         }
84
         p {
85
         	padding: 0;
86
          	margin: 0;
87
         }
88
         a {
89
             color:#FFFFFF;
90
         }
91
         img {
92
             height: 24px;
93
             width: 24px;
94
         }
95
         @media (max-width: 480px) {
96
             .truncate {
97
                 max-width: 300px;
98
             }
99
         }
100
    </style>
101
  </head>
102
  <body>
103
    <div class="stats-container">
104
      <h1>Steve's Pi</h1>
105
      <p>
106
        Welcome to a live feed of my Raspberry Pi! It sits on my desk and runs
107
        multiple small services such as
108
        <a href="https://ipfs.io" target="_blank">IPFS</a> and
109
        <a href="https://radicle.xyz" target="_blank">Radicle</a>.
110
      </p>
111
      <div class="ipfs-div">
112
        <img src="https://dweb.mypinata.cloud/ipfs/QmbvEEN8zY657JC6wC2piMygmHcEKhwT5gkNWUs2qcnwKb" alt="ipfs-cube">
113
        <h3>IPFS Node</h3>
114
      </div>
115
      <p>RepoSize: <span id="repoSize">-</span></p>
116
      <p>Objects: <span id="objects">-</span></p>
117
      <p>Bandwidth In: <span id="rateIn">-</span></p>
118
      <p>Bandwidth Out: <span id="rateOut">-</span></p>
119
      <p>Total Data In: <span id="totalIn">-</span></p>
120
      <p>Total Data Out: <span id="totalOut">-</span></p>
121
      <div class="system-info">
122
        <div class="ipfs-div">
123
            <img src="https://dweb.mypinata.cloud/ipfs/QmVtXdzGAQYWYMFGCZR9XV1NFpT941F234Q4oaANTvPeqb" alt="radicle-alien">
124
          <h3>Radicle Node</h3>
125
        </div>
126
        <p>ID: <span class="truncate" id="radId">-</span></p>
127
        <p>Agent: <span id="radAgent">-</span></p>
128
        <p>Status: <span id="radStatus">-</span></p>
129
        <p>Repos: <span id="radRepos">-</span></p>
130
        <p>Seeding Policy: <span id="radPolicy">-</span></p>
131
      </div>
132
      <div class="system-info">
133
        <div class="ipfs-div">
134
          <img src="https://dweb.mypinata.cloud/ipfs/Qmaz5ih9noiMqNnAE5681cCPwGnomopKLUfF2LaSNmuV4P" alt="raspberry-pi-logo" />
135
          <h3>System Information</h3>
136
        </div>
137
        <p>OS: <span id="os">-</span></p>
138
        <p>Kernel: <span id="kernel">-</span></p>
139
        <p>Uptime: <span id="uptime">-</span></p>
140
        <p>CPU Model: <span id="cpuModel">-</span></p>
141
        <p>CPU Usage: <span id="cpuUsage">-</span>%</p>
142
        <p>
143
          Memory: <span id="memoryUsed">-</span>/<span id="memoryTotal">-</span>
144
        </p>
145
      </div>
146
      <div class="link-container">
147
        <a
148
          class="source-link"
149
          href="http://github.com/stevedylandev/pi-widget"
150
          target="_blank"
151
          >Source Code</a
152
        >
153
        <a class="source-link" href="https://stevedylan.dev" target="_blank"
154
          >Homepage</a
155
        >
156
      </div>
157
    </div>
158
    <script>
159
      function formatBytes(bytes, decimals = 2) {
160
        if (bytes === 0) return "0 Bytes";
161
162
        const k = 1024;
163
        const dm = decimals < 0 ? 0 : decimals;
164
        const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
165
166
        const i = Math.floor(Math.log(bytes) / Math.log(k));
167
168
        return (
169
          parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i]
170
        );
171
      }
172
173
      function formatBitrate(bits) {
174
        if (bits < 1000) {
175
          return bits.toFixed(2) + " bps";
176
        } else if (bits < 1000000) {
177
          return (bits / 1000).toFixed(2) + " Kbps";
178
        } else {
179
          return (bits / 1000000).toFixed(2) + " Mbps";
180
        }
181
      }
182
183
      const evtSource = new EventSource("/events");
184
185
      evtSource.onopen = function (event) {
186
        console.log("SSE connection opened");
187
      };
188
189
      evtSource.onerror = function (event) {
190
        console.error("SSE connection error:", event);
191
      };
192
193
      evtSource.onmessage = function (event) {
194
        console.log("Received data:", event.data);
195
        try {
196
          const data = JSON.parse(event.data);
197
          // IPFS
198
          document.getElementById("repoSize").textContent = formatBytes(
199
            data.RepoSize,
200
          );
201
          document.getElementById("objects").textContent =
202
            data.NumObjects.toLocaleString();
203
          document.getElementById("rateIn").textContent = formatBitrate(
204
            data.RateIn,
205
          );
206
          document.getElementById("rateOut").textContent = formatBitrate(
207
            data.RateOut,
208
          );
209
          document.getElementById("totalIn").textContent = formatBytes(
210
            data.TotalIn,
211
          );
212
          document.getElementById("totalOut").textContent = formatBytes(
213
            data.TotalOut,
214
          );
215
          // Rad Node
216
          document.getElementById("radId").textContent = data.id;
217
          document.getElementById("radAgent").textContent = data.agent;
218
          document.getElementById("radStatus").textContent = data.state;
219
          document.getElementById("radPolicy").textContent = data.config.seedingPolicy.default;
220
          document.getElementById("radRepos").textContent = data.repos.total.toString()
221
222
          // System
223
          document.getElementById("os").textContent = data.os;
224
          document.getElementById("kernel").textContent = data.kernel;
225
          document.getElementById("uptime").textContent = data.uptime;
226
          document.getElementById("cpuModel").textContent = data.cpuModel;
227
          document.getElementById("cpuUsage").textContent =
228
            data.cpuUsage.toFixed(2);
229
          document.getElementById("memoryUsed").textContent = formatBytes(
230
            data.memoryUsed,
231
          );
232
          document.getElementById("memoryTotal").textContent = formatBytes(
233
            data.memoryTotal,
234
          );
235
        } catch (error) {
236
          console.error("Error parsing or updating data:", error);
237
        }
238
      };
239
    </script>
240
  </body>
241
</html>