app.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. "use strict";
  2. /**
  3. * Constants for application
  4. *
  5. * manifestList is the url that contains all possible manifests
  6. * pollTime is used to check for changes in the serial ports
  7. *
  8. * @type {{manifestList: string, pollTime: number}}
  9. */
  10. const CONSTANTS = {
  11. manifestList: "http://flasher.thingssdk.com/v1/manifest-list.json",
  12. pollTime: 1000
  13. };
  14. var last_notification = "";
  15. /************************
  16. * Backend dependencies.
  17. * Note: Paths are relative to index.html not app.js
  18. ************************/
  19. const SerialScanner = require("../back-end/serial_scanner");
  20. const PortSelect = require("./js/port_select");
  21. const prepareBinaries = require("../back-end/prepare_binaries");
  22. const log = require("../back-end/logger");
  23. const RomComm = require("../back-end/rom_comm");
  24. const serialScanner = new SerialScanner();
  25. /************************
  26. * UI Elements
  27. ************************/
  28. const flashButton = $("flash-button");
  29. const appStatus = $("status");
  30. const portsSelect = new PortSelect($("ports"));
  31. const manifestsSelect = $("manifests");
  32. const progressHolder = $("progressBar");
  33. const progressBar = $("progress");
  34. const form = $("form");
  35. /************************
  36. * Utility Functions
  37. ************************/
  38. /**
  39. * Simple helper returns HTML element from an element's id
  40. *
  41. * @param id a string of the id of an HTML element
  42. * @returns {Element}
  43. */
  44. function $(id) { return document.getElementById(id); }
  45. /**
  46. * Processes the response from an HTTP fetch and returns the JSON promise.
  47. *
  48. * @param response from a fetch call
  49. * @returns {Promise}
  50. */
  51. function processJSON(response) {
  52. return response.json();
  53. }
  54. /************************
  55. * Handle UI
  56. ************************/
  57. flashButton.addEventListener("click", (event) => {
  58. disableInputs();
  59. fetch(manifestsSelect.value)
  60. .then(processJSON)
  61. .then(flashWithManifest);
  62. });
  63. /************************
  64. * Manage serial port events
  65. ************************/
  66. serialScanner.on("ports", (ports) => {
  67. portsSelect.addAll(ports);
  68. readyToFlash();
  69. });
  70. serialScanner.on("deviceAdded", (port) => {
  71. portsSelect.add(port);
  72. new Notification(`Added: ${port}!`);
  73. });
  74. serialScanner.on("deviceRemoved", (port) => {
  75. portsSelect.remove(port);
  76. new Notification(`Removed: ${port}!`);
  77. });
  78. serialScanner.on("error", onError);
  79. /**
  80. * Updates UI to say it's ready
  81. */
  82. function readyToFlash() {
  83. progressHolder.style.display = "none";
  84. form.style.display = "block";
  85. appStatus.textContent = "Ready";
  86. enableInputs();
  87. }
  88. /**
  89. * Enabled the serial port SELECT and flash BUTTON elements.
  90. */
  91. function enableInputs() {
  92. portsSelect.disabled = false;
  93. manifestsSelect.disabled = false;
  94. flashButton.disabled = false;
  95. }
  96. function disableInputs() {
  97. portsSelect.disabled = true;
  98. manifestsSelect.disabled = true;
  99. flashButton.disabled = true;
  100. }
  101. /**
  102. * Generic catch all error. Shows notification at the moment.
  103. * @param error
  104. */
  105. function onError(error){
  106. if(last_notification !== error.message) {
  107. last_notification = error.message;
  108. new Notification(last_notification);
  109. }
  110. appStatus.textContent = error.message;
  111. }
  112. function generateManifestList(manifestsJSON) {
  113. manifestsJSON.options.forEach((option) => {
  114. option.versions.forEach((version) => {
  115. const optionElement = document.createElement("option");
  116. optionElement.textContent = `${option.name} - ${version.version}`;
  117. optionElement.value = version.manifest;
  118. manifestsSelect.appendChild(optionElement);
  119. manifestsSelect.disabled = false;
  120. });
  121. });
  122. }
  123. function getManifests() {
  124. appStatus.textContent = "Getting latest manifests.";
  125. fetch(CONSTANTS.manifestList)
  126. .then(processJSON)
  127. .then(generateManifestList).catch(error => {
  128. setTimeout(getManifests, pollTime);
  129. });
  130. }
  131. function flashWithManifest(manifest) {
  132. form.style.display = "none";
  133. progressHolder.style.display = "block";
  134. appStatus.textContent = `Flashing ${portsSelect.value}`;
  135. const numberOfSteps = manifest.flash.length * 2;
  136. let currectStepNumber = 1;
  137. prepareBinaries(manifest, (err, flashSpec) => {
  138. if(err) throw err;
  139. const esp = new RomComm({
  140. portName: portsSelect.value,
  141. baudRate: 115200
  142. });
  143. esp.on('progress', (progress) => {
  144. const flashPercent = Math.round((progress.details.flashedBytes/progress.details.totalBytes) * 100);
  145. const processSoFar = 50; //From download and extracting.
  146. const flashProcess = flashPercent / 2; //To add to the overall progress
  147. updateProgressBar(processSoFar + flashProcess);
  148. appStatus.textContent = `${progress.display} - ${flashPercent}%`;
  149. });
  150. esp.open().then((result) => {
  151. appStatus.textContent = `Flashing device connected to ${portsSelect.value}`;
  152. let promise = Promise.resolve();
  153. return esp.flashSpecifications(flashSpec)
  154. .then(() => esp.close())
  155. .then((result) => {
  156. new Notification("Flash Finished!");
  157. readyToFlash();
  158. log.info("Flashed to latest Espruino build!", result);
  159. });
  160. }).catch((error) => {
  161. new Notification("An error occured during flashing.");
  162. readyToFlash();
  163. log.error("Oh noes!", error);
  164. });
  165. }).on("entry", (progress) => {
  166. //For the download/extract progress. The other half is flashing.
  167. const extractPercent = Math.round((currectStepNumber++/numberOfSteps) * 50);
  168. updateProgressBar(extractPercent);
  169. appStatus.textContent = progress.display;
  170. });
  171. }
  172. function updateProgressBar(percent) {
  173. progressBar.style.width = `${percent}%`;
  174. }
  175. /**
  176. * Get's manifest list for possibilities for flashing,
  177. * scans serial ports and sets up timer for checking for changes.
  178. */
  179. function start() {
  180. getManifests();
  181. serialScanner.scan();
  182. setInterval(serialScanner.checkForChanges.bind(serialScanner), CONSTANTS.pollTime);
  183. }
  184. /**
  185. * Start Application
  186. */
  187. start();