app.js 6.3 KB

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