index.html 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  7. <title>ESPWoL</title>
  8. <style>
  9. body{
  10. font-family: Arial, sans-serif;
  11. color: #262626;
  12. }
  13. .box {
  14. display: flex;
  15. align-items: center;
  16. justify-content: center;
  17. }
  18. .wallpaper{
  19. position: absolute;
  20. width: 100%;
  21. height: 100%;
  22. left: 0;
  23. top: 0;
  24. background-color: #ffffff;
  25. }
  26. .main{
  27. width: 480px;
  28. height: calc(90vh - 2em);
  29. padding: 1em;
  30. -webkit-box-shadow: 10px 10px 18px 0px rgba(0,0,0,0.14);
  31. -moz-box-shadow: 10px 10px 18px 0px rgba(0,0,0,0.14);
  32. box-shadow: 10px 10px 18px 0px rgba(0,0,0,0.14);
  33. border-radius: 0.6em;
  34. border: 1px solid rgb(172, 172, 172);
  35. position: relative;
  36. background-color: white;
  37. }
  38. .divider{
  39. width: calc(100% - 4em);
  40. padding-top: 0.4em;
  41. margin-bottom: 0.4em;
  42. border-bottom: 1px solid rgb(226, 226, 226);
  43. padding-right: 2em;
  44. padding-left: 2em;
  45. }
  46. small{
  47. color: rgb(59, 59, 59);
  48. }
  49. .codeblock{
  50. padding: 0.6em;
  51. background-color: #242424;
  52. color: white;
  53. }
  54. /* Properties Table */
  55. .table_component {
  56. overflow: auto;
  57. width: 100%;
  58. }
  59. .table_component table {
  60. border: 1px solid #dededf;
  61. height: 100%;
  62. width: 100%;
  63. table-layout: fixed;
  64. border-collapse: collapse;
  65. border-spacing: 1px;
  66. text-align: left;
  67. }
  68. .table_component caption {
  69. caption-side: top;
  70. text-align: left;
  71. }
  72. .table_component th {
  73. border: 1px solid #dededf;
  74. background-color: #eceff1;
  75. color: #000000;
  76. padding: 5px;
  77. }
  78. .table_component td {
  79. border: 1px solid #dededf;
  80. background-color: #ffffff;
  81. color: #000000;
  82. padding: 5px;
  83. }
  84. /* Front Panel UI emulation */
  85. .controlEmu{
  86. position: absolute;
  87. right: 1.2em;
  88. bottom: 1.2em;
  89. padding: 1em;
  90. border-radius: 0.6em;
  91. background-color: #ececec;
  92. border: 1px solid rgb(197, 197, 197);
  93. }
  94. /* Power Button */
  95. #pwrbtn {
  96. display:block;
  97. height: 80px;
  98. width: 80px;
  99. border-radius: 50%;
  100. border: 1px solid rgba(156, 156, 156, 0.5);
  101. cursor: pointer;
  102. box-shadow: 10px 10px 18px 0px rgba(0,0,0,0.38);
  103. background-color: #f8f8f8;
  104. }
  105. #pwrbtn:hover{
  106. background-color: #c7c7c7;
  107. }
  108. #pwrbtn:active{
  109. background-color: #868686;
  110. box-shadow: inset 10px 10px 18px 0px rgba(0,0,0,0.38);
  111. }
  112. #pwrbtn svg{
  113. margin-top: 1.5em;
  114. margin-left: 0.1em;
  115. }
  116. /* Reset Button */
  117. #rstbtn {
  118. display:block;
  119. height: 40px;
  120. width: 40px;
  121. border-radius: 50%;
  122. border: 1px solid rgba(156, 156, 156, 0.5);
  123. cursor: pointer;
  124. margin-bottom: 1em;
  125. box-shadow: 10px 10px 18px 0px rgba(0,0,0,0.38);
  126. }
  127. #rstbtn svg{
  128. margin-top: 0.5em;
  129. margin-left: 0.1em;
  130. }
  131. #rstbtn:hover{
  132. background-color: #c7c7c7;
  133. }
  134. #rstbtn:active{
  135. background-color: #868686;
  136. box-shadow: inset 10px 10px 18px 0px rgba(0,0,0,0.38);
  137. }
  138. /* LEDs */
  139. .leds{
  140. margin-bottom: 1em;
  141. }
  142. .ledlabel{
  143. color: rgb(83, 83, 83);
  144. font-size: 0.8em;
  145. pointer-events: none;
  146. user-select: none;
  147. }
  148. .led {
  149. display:block;
  150. height: 10px;
  151. width: 10px;
  152. border-radius: 50%;
  153. background-color: #242424;
  154. }
  155. /* status blinker */
  156. #statusBlinker{
  157. position: absolute;
  158. left: 1em;
  159. bottom: 1em;
  160. width: 1em;
  161. height: 2px;
  162. background-color: rgb(95, 95, 95);
  163. }
  164. #statusBlinker.interval{
  165. background-color: rgb(235, 235, 235);
  166. }
  167. </style>
  168. </head>
  169. <body>
  170. <div class="wallpaper"></div>
  171. <br>
  172. <div class="box">
  173. <div class="main">
  174. <h2>🔌 ESPWoL Control Panel</h2>
  175. <p>Press a button on the bottom to power up / shutdown / reset your computer wirelessly</p>
  176. <div class="table_component" role="region" tabindex="0">
  177. <table>
  178. <thead>
  179. <tr>
  180. <th>Properties</th>
  181. <th>Status</th>
  182. </tr>
  183. </thead>
  184. <tbody>
  185. <tr>
  186. <td>
  187. <div>WLAN IP<br></div>
  188. </td>
  189. <td id="wlanip"></td>
  190. </tr>
  191. <tr>
  192. <td>
  193. <div>mDNS Address<br></div>
  194. </td>
  195. <td id="mdnsaddr"></td>
  196. </tr>
  197. <tr>
  198. <td>Power Status<br></td>
  199. <td id="powerLED">OFF</td>
  200. </tr>
  201. <tr>
  202. <td>HDD Status<br></td>
  203. <td id="hddStatus">IDLE</td>
  204. </tr>
  205. </tbody>
  206. </table>
  207. </div>
  208. <h3>🤖 RESTFUL API / Magic Packet</h3>
  209. <p>You can also access the power status and controls via RESTFUL API requests or <a href="./wol">send WoL magic packet</a></p>
  210. <div class="codeblock"><code>
  211. GET /api/status <br>
  212. POST /api/power <br>
  213. POST /api/reset <br>
  214. </code></div>
  215. <br>
  216. <div class="divider"></div>
  217. <small>ESPWoL | Deisnged by tobychui 2023 - 2024</small>
  218. <div class="controlEmu" align="center">
  219. <div id="rstbtn" title="Reset Computer">
  220. <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M440-122q-121-15-200.5-105.5T160-440q0-66 26-126.5T260-672l57 57q-38 34-57.5 79T240-440q0 88 56 155.5T440-202v80Zm80 0v-80q87-16 143.5-83T720-440q0-100-70-170t-170-70h-3l44 44-56 56-140-140 140-140 56 56-44 44h3q134 0 227 93t93 227q0 121-79.5 211.5T520-122Z"/></svg>
  221. </div>
  222. <div class="leds">
  223. <div class="ledlabel">HDD <div class="led hddled"></div></div><br>
  224. <div class="ledlabel">PWR <div class="led powerled"></div></div>
  225. </div>
  226. <div id="pwrbtn" title="Power On / Off">
  227. <svg id="pwricon" fill="#eb4034" xmlns="http://www.w3.org/2000/svg" height="32" viewBox="0 -960 960 960" width="32"><path d="M440-440v-400h80v400h-80Zm40 320q-74 0-139.5-28.5T226-226q-49-49-77.5-114.5T120-480q0-80 33-151t93-123l56 56q-48 40-75 97t-27 121q0 116 82 198t198 82q117 0 198.5-82T760-480q0-64-26.5-121T658-698l56-56q60 52 93 123t33 151q0 74-28.5 139.5t-77 114.5q-48.5 49-114 77.5T480-120Z"/></svg>
  228. </div>
  229. </div>
  230. <div id="statusBlinker"></div>
  231. </div>
  232. </div>
  233. <script>
  234. /*
  235. The update interval will change based on the current status of the remote
  236. computer. By default, it update the status LED every seconds. When the
  237. remote is powered on, it update with 100ms delay between response and new requests.
  238. */
  239. let updateInterval = 1000;
  240. let statusBlinker = false; //Just an indicator to show connection is established
  241. //Set the power status and hdd led status to off
  242. togglePowerState(false);
  243. toggleHDDLED(false);
  244. /* Get the device ip and mdns */
  245. function initIPInfo(){
  246. fetch('/ipaddr', {
  247. method: 'GET'
  248. })
  249. .then(response => {
  250. if (!response.ok) {
  251. throw new Error('Network error');
  252. document.getElementById("wlanip").innerText = "192.168.4.1";
  253. document.getElementById("mdnsaddr").innerText = "Unknown";
  254. }
  255. return response.json();
  256. })
  257. .then(data => {
  258. document.getElementById("wlanip").innerText = data.ipaddr;
  259. document.getElementById("mdnsaddr").innerText = data.mdns;
  260. });
  261. }
  262. initIPInfo();
  263. /* Read the status from server */
  264. function updateLEDStatus(){
  265. fetch('/status', {
  266. method: 'GET'
  267. })
  268. .then(response => {
  269. if (!response.ok) {
  270. throw new Error('Network error');
  271. updateInterval = 1000;
  272. setTimeout(updateLEDStatus, updateInterval);
  273. }
  274. return response.json();
  275. })
  276. .then(data => {
  277. //Update the power LED and update interval
  278. togglePowerState(data.pwr);
  279. if (data.pwr){
  280. updateInterval = 100;
  281. }else{
  282. updateInterval = 1000;
  283. }
  284. setTimeout(updateLEDStatus, updateInterval);
  285. //Update the hdd led
  286. toggleHDDLED(data.hdd);
  287. //Update the status blinker
  288. statusBlinker = !statusBlinker;
  289. var blinker = document.getElementById("statusBlinker");
  290. if (statusBlinker){
  291. blinker.classList.add("interval");
  292. }else{
  293. blinker.classList.remove("interval");
  294. }
  295. })
  296. }
  297. setTimeout(updateLEDStatus, updateInterval);
  298. /* Status LED Rendering */
  299. function togglePowerState(powerIsOn = true){
  300. var powerLedIndicator = document.querySelectorAll('.powerled')[0];
  301. if (powerIsOn){
  302. document.getElementById("powerLED").innerHTML = "🟢 ON";
  303. document.getElementById("pwricon").setAttribute("fill", "#7fe38b");
  304. powerLedIndicator.style.backgroundColor = '#16c60c';
  305. }else{
  306. document.getElementById("powerLED").innerHTML = "⚫ OFF";
  307. document.getElementById("pwricon").setAttribute("fill", "#eb4034");
  308. powerLedIndicator.style.backgroundColor = '#383838';
  309. }
  310. }
  311. function toggleHDDLED(hddLEDOn = true){
  312. var hddLedIndicator = document.querySelectorAll('.hddled')[0];
  313. if (hddLEDOn){
  314. document.getElementById("hddStatus").innerHTML = "🔵 R/W";
  315. hddLedIndicator.style.backgroundColor = '#0078d7';
  316. }else{
  317. document.getElementById("hddStatus").innerHTML = "⚫ IDLE";
  318. hddLedIndicator.style.backgroundColor = '#383838';
  319. }
  320. }
  321. /* Button Emulations */
  322. function handlePowerButtonPress(){
  323. fetch('/power/press', {
  324. method: 'GET'
  325. }).then(response => {
  326. if (!response.ok) {
  327. console.log("Error when trying to press power button")
  328. }
  329. });
  330. }
  331. function handlePowerButtonRelease(){
  332. fetch('/power/release', {
  333. method: 'GET'
  334. }).then(response => {
  335. if (!response.ok) {
  336. console.log("Error when trying to press power button")
  337. }
  338. });
  339. }
  340. function handleResetButtonPress(){
  341. fetch('/reset/press', {
  342. method: 'GET'
  343. }).then(response => {
  344. if (!response.ok) {
  345. console.log("Error when trying to press reset button")
  346. }
  347. });
  348. }
  349. function handlResetButtonRelease(){
  350. fetch('/reset/release', {
  351. method: 'GET'
  352. }).then(response => {
  353. if (!response.ok) {
  354. console.log("Error when trying to press reset button")
  355. }
  356. });
  357. }
  358. /* Bind events for the buttons */
  359. document.getElementById("pwrbtn").addEventListener("mousedown", handlePowerButtonPress);
  360. document.getElementById("pwrbtn").addEventListener("touchstart", handlePowerButtonPress);
  361. document.getElementById("pwrbtn").addEventListener("mouseup", handlePowerButtonRelease);
  362. document.getElementById("pwrbtn").addEventListener("touchend", handlePowerButtonRelease);
  363. document.getElementById("rstbtn").addEventListener("mousedown", handleResetButtonPress);
  364. document.getElementById("rstbtn").addEventListener("touchstart", handleResetButtonPress);
  365. document.getElementById("rstbtn").addEventListener("mouseup", handlResetButtonRelease);
  366. document.getElementById("rstbtn").addEventListener("touchend", handlResetButtonRelease);
  367. </script>
  368. </body>
  369. </html>