Guest User

index.h

a guest
Apr 23rd, 2025
104
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 11.64 KB | None | 0 0
  1. const char MAIN_page[] PROGMEM = R"=====(
  2. <!doctype html>
  3. <html lang="en">
  4. <head>
  5. <meta charset="utf-8">
  6. <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  7. <meta name="description" content="">
  8. <meta name="author" content="">
  9. <link rel="icon" href="https://www.ikea.com/favicon.ico">
  10. <title>IKEA Skarsta/Trotten Dashboard</title>
  11. <link rel="stylesheet" href="https://getbootstrap.com/docs/4.0/dist/css/bootstrap.min.css">
  12. <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.css">
  13. <style>
  14. html {
  15. font-size: 14px;
  16. }
  17. @media (min-width: 768px) {
  18. html {
  19. font-size: 16px;
  20. }
  21. }
  22. .container {
  23. max-width: 960px;
  24. }
  25. .pricing-header {
  26. max-width: 700px;
  27. }
  28. .card-deck .card {
  29. min-width: 220px;
  30. }
  31. .border-top { border-top: 1px solid #e5e5e5; }
  32. .border-bottom { border-bottom: 1px solid #e5e5e5; }
  33. .box-shadow { box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05); }
  34.  
  35. #mode-container {
  36. margin-bottom: 10px;
  37. }
  38.  
  39. .mode-button {
  40. color: white !important;
  41. }
  42.  
  43. .motor-button {
  44. color: #007bff !important;
  45. }
  46. .motor-button:hover, .mode-trash-button:hover {
  47. color: white !important;
  48. }
  49. </style>
  50. </head>
  51. <body>
  52. <div class="d-flex flex-column flex-md-row align-items-center p-3 px-md-4 mb-3 bg-white border-bottom box-shadow">
  53. <h5 class="my-0 mr-md-auto font-weight-normal">IKEA Skarsta/Trotten</h5>
  54. </div>
  55. <div class="pricing-header px-3 py-3 pt-md-5 pb-md-4 mx-auto text-center">
  56. <h1 class="display-4">Dashboard</h1>
  57. </div>
  58. <div class="container">
  59. <div class="card-deck mb-3 text-center">
  60. <div class="card mb-4 box-shadow">
  61. <div class="card-header">
  62. <h4 class="my-0 font-weight-normal">Controls</h4>
  63. </div>
  64. <div class="card-body">
  65. <ul class="list-unstyled mt-3 mb-4">
  66. <li>Manual height adjustment</li>
  67. </ul>
  68. <a type="button" class="btn btn-lg btn-block btn-outline-primary motor-button" onclick="fetch('/motor/up').then().catch(console.error('Error while communicating with the table'))"><i class="bi bi-arrow-up-square-fill"></i></a>
  69. <a type="button" class="btn btn-lg btn-block btn-outline-primary motor-button" onclick="fetch('/motor/stop').then().catch(console.error('Error while communicating with the table'))"><i class="bi bi-pause-btn-fill"></i></a>
  70. <a type="button" class="btn btn-lg btn-block btn-outline-primary motor-button" onclick="fetch('/motor/down').then().catch(console.error('Error while communicating with the table'))"><i class="bi bi-arrow-down-square-fill"></i></a>
  71. </div>
  72. </div>
  73. <div class="card mb-4 box-shadow">
  74. <div class="card-header">
  75. <h4 class="my-0 font-weight-normal">Modes</h4>
  76. </div>
  77. <div class="card-body">
  78. <ul class="list-unstyled mt-3 mb-4">
  79. <li>Adjustment via modes</li>
  80. </ul>
  81. <div id="mode-container" class=""><!-- This is the element into which the modes from the local storage will be loaded --></div>
  82.  
  83. <div class="input-group mb-3">
  84. <input id="input" type="number" class="form-control" placeholder="Height in cm (min: 70, max: 120)" aria-label="" min="70" max="120">
  85. <div class="input-group-append">
  86. <button onclick="addMode(getElementById('input').value)" class="btn btn-outline-secondary" type="button"><i class="bi bi-plus-square-fill"></i></button>
  87. </div>
  88. </div>
  89.  
  90. </div>
  91. </div>
  92.  
  93. </div>
  94. </div>
  95. <div class="container">
  96. <div class="card mb-4 box-shadow h-100">
  97. <div class="card-header">
  98. <h4 class="my-0 font-weight-normal">Height</h4>
  99. </div>
  100. <div class="card-body">
  101. <div class="progress">
  102. <div id="height-value" class="progress-bar" role="progressbar" style="width: 0%;"></div>
  103. </div>
  104. </div>
  105. </div>
  106. </div>
  107.  
  108. <script>
  109. const LOCALSTORAGE_ITEM_NAME = "modes"; // identifier of the saved modes
  110. const DEFAULT_MODES = ["70", "95", "120"]; // local storage will saves everything as strings and these values will be URI parameters so it is fine to not use integers
  111. const MODE_CONTAINER_ELEMENT_ID = "mode-container"; // Note: if you change this, please also change the corresponding id in HTML element
  112. const HEIGHT_VALUE_ELEMENT_ID = "height-value"; // Note: if you change this, please also change the corresponding id in HTML element
  113. const MIN_MODE_HEIGHT = 70;
  114. const MAX_MODE_HEIGHT = 120;
  115.  
  116.  
  117. /*
  118. * Takes care of correctly setting the table height in the progess bar.
  119. * The height will be shown in percentage -> 100% = max height
  120. */
  121. const setHeightInProgessBar = (height) => {
  122. let progessBar = document.getElementById(HEIGHT_VALUE_ELEMENT_ID);
  123. progessBar.innerHTML = height + " cm"; // add text to the progress bar
  124. progessBar.style.width = ((height / MAX_MODE_HEIGHT) * 100) + "%"; // set progress by calculating the percentage of the current height
  125. }
  126.  
  127.  
  128. /*
  129. * Returns the height of the table, measured with the ultrasonic sensor, by performing a XMLHttp request
  130. */
  131. const getHeight = () => {
  132. let request = new XMLHttpRequest();
  133.  
  134. request.onreadystatechange = function() {
  135. if (this.readyState == 4 && this.status == 200) {
  136. console.log("Received height: " + this.responseText); // Check this value
  137. setHeightInProgessBar(this.responseText);
  138. }
  139. };
  140. request.open("GET", "height", true);
  141. request.send();
  142. }
  143.  
  144.  
  145. /*
  146. * Returns the saved modes from the local storage as asn array of strings.
  147. * Example -> ["75", "125", "150"]
  148. */
  149. const getModes = () => {
  150. let modes = localStorage.getItem(LOCALSTORAGE_ITEM_NAME);
  151. // check if the item exists, otherwise initialize it with the default values
  152. if (modes) {
  153. return JSON.parse(modes);
  154. }
  155. else {
  156. localStorage.setItem(LOCALSTORAGE_ITEM_NAME, JSON.stringify(DEFAULT_MODES));
  157. return DEFAULT_MODES;
  158. }
  159. }
  160.  
  161.  
  162. /*
  163. * Removes the given mode from the local storage and performs a page
  164. * refresh so that the deleted mode won't be shown
  165. */
  166. const deleteMode = (id) => {
  167. let currentModes = getModes();
  168. // remove one entry at index 'id'
  169. currentModes.splice(id, 1);
  170. // save to localstorage
  171. localStorage.setItem(LOCALSTORAGE_ITEM_NAME, JSON.stringify(currentModes));
  172. // reload the page
  173. location.reload(true);
  174. }
  175.  
  176.  
  177. /*
  178. * Onclick handler for the add mode button. Adds a new mode to the
  179. * locally saved mode if it is withing the range.
  180. */
  181. const addMode = (height) => {
  182. let currentModes = getModes();
  183. // no need to add a new mode if the height already exists in another mode
  184. if(currentModes.indexOf(height.toString()) != -1) {
  185. return;
  186. }
  187.  
  188. // mode will only be saved if it is withing range
  189. if (height >= MIN_MODE_HEIGHT && height <= MAX_MODE_HEIGHT){
  190. // append the new height
  191. currentModes.push(height.toString());
  192.  
  193. // sort the array before saving it
  194. currentModes.sort(function(left, right) {
  195. left = parseInt(left); // convert string to int in order to compare
  196. right = parseInt(right);
  197. return left - right;
  198. });
  199.  
  200. // save the new and sorted mode array
  201. localStorage.setItem(LOCALSTORAGE_ITEM_NAME, JSON.stringify(currentModes));
  202. }
  203.  
  204. // reload the page to see the new mode
  205. location.reload(true);
  206. }
  207.  
  208.  
  209. /*
  210. * Creates a button for which represents the mode. This button is also clickable and will do a request to the server for changing the tables height.
  211. * Example for an created element: <a type="button" class="btn btn-lg btn-block btn-primary">Mode 3 - 150 cm</a>
  212. * The onclick event of a button does a HTTP request to communicate with the table, so the height can be changed.
  213. */
  214. const createModeElement = (id, height) => {
  215. // create a button group which shall consist of the mode button and a delete mode button next to it
  216. let buttonGroup = document.createElement("div");
  217. buttonGroup.className = "btn-group btn-block";
  218. buttonGroup.role = "group";
  219.  
  220. // create the mode button
  221. let button = document.createElement("a");
  222. button.type = "button";
  223. button.className = "btn btn-primary col-sm-10 mode-button";
  224. button.onclick = function() { fetch("/height/" + height).then().catch(console.error("Error while communicating with the table")); };
  225. button.innerHTML = "Mode " + (id + 1) + " - " + height + " cm"; // Start counting at 1 -> "Mode 1 - 75 cm"
  226.  
  227. // create the delete mode button
  228. let deleteButton = document.createElement("a");
  229. deleteButton.type = "button";
  230. deleteButton.className = "btn btn-outline-primary col-sm-2 mode-trash-button";
  231. deleteButton.onclick = function() { deleteMode(id); }; // handler for deleting this mode
  232. deleteButton.innerHTML = "<i class='bi bi-trash'></i>"; // "trash" icon
  233.  
  234. buttonGroup.appendChild(button);
  235. buttonGroup.appendChild(deleteButton);
  236. return buttonGroup; // return the element which now holds two buttons and all necessary info
  237. }
  238.  
  239.  
  240. /*
  241. * Adds all passed modes to the modes container so that they are being displayed.
  242. */
  243. const displayModes = (modes) => {
  244. let modeContainer = document.getElementById(MODE_CONTAINER_ELEMENT_ID);
  245. // Create and immediately append an element for each mode as a child of the container
  246. for (i in modes) {
  247. let newMode = createModeElement(parseInt(i), modes[i]); // Create the element
  248. modeContainer.appendChild(newMode); // Add element to DOM
  249. }
  250. }
  251.  
  252.  
  253. // get all locally saved modes
  254. let foundModes = getModes();
  255.  
  256. // display all of the locally saved modes
  257. displayModes(foundModes);
  258.  
  259. // request the current table height every 2000 ms
  260. setInterval(function() {
  261. getHeight(); // AJAX call
  262. }, 2000);
  263. </script>
  264.  
  265. </body>
  266. </html>
  267. )=====";
  268.  
Add Comment
Please, Sign In to add comment