diff --git a/.babelrc b/.babelrc index 47d6aadc..3e1aaa9e 100644 --- a/.babelrc +++ b/.babelrc @@ -2,19 +2,27 @@ "env": { "commonjs": { "presets": [ - ["env", { - "useBuiltIns": false - }] + [ + "@babel/env", + { + "useBuiltIns": false + } + ] ] }, "es": { "presets": [ - ["env", { - "useBuiltIns": false, - "modules": false - }] + [ + "@babel/env", + { + "useBuiltIns": false, + "modules": false + } + ] ] } }, - "plugins": ["transform-object-rest-spread"] -} + "plugins": [ + "@babel/proposal-object-rest-spread" + ] +} \ No newline at end of file diff --git a/.eslintrc b/.eslintrc index 177fe664..a3f23853 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,5 +1,8 @@ { "parser": "babel-eslint", + "plugins": [ + "react" + ], "rules": { "max-len": 0, "comma-dangle": 0, @@ -13,7 +16,8 @@ "arrow-parens": 0, "consistent-return": 0, "no-useless-escape": 0, - "no-underscore-dangle": 0 + "no-underscore-dangle": 0, + "react/jsx-uses-vars": 2 }, "extends": "airbnb-base", "env": { diff --git a/.gitignore b/.gitignore index 22641d8c..c3e66490 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ lib dist es .tmp +misc diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 00000000..158c0064 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +v14.16.0 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..93d98070 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,40 @@ +# Changelog +All notable changes to this project will be documented in this file. + +## [2.11.0] - 2022-09-27 +The main goal of this release is to improve support for the Web Serial API on ChromeOS. +Other platforms should not be affected. + +### Changed +- When using Web Serial API, the interactions between the client library + (as an example, the Arduino `arduino-chromeos-uploader` libray) has been simplified. +- A new parameter `dialogCustomizations` has been added to the upload functionality. It's used + to provide custom confirmation dialogs when using the Web Serial API. + It has no effect with other daemons. + +### Removed +- `cdcReset` functionality, now it's embedded in the `upload` functionality + in the Web Serial daemon. +### Changed + +## [2.10.1] - 2022-09-08 + +### Changed +- Fixed a bug released in 2.9.1 caused by the wrong assumption that the build filename is always at the end of the command line. This fix makes the library backward compatible with older ESP boards. + +## *DEPRECATED* [2.9.1] - 2022-09-06 +### Added +- Added support for ESP32 boards + +## [2.9.0] - 2022-06-06 +### Added +- Added support for "Arduino RP2040 Connect" board +### Changed +- Improved support for Chrome's Web Serial API on ChromeOS. Other operating systems should not be affected. +- Simplified the communication with the Web Serial API via a messaging system which simulates + the [postMessage](https://developer.chrome.com/docs/extensions/reference/runtime/#method-Port-postMessage) function available in the Chrome App Daemon (see `chrome-app-daemon.js`). + +## [2.8.0] - 2022-03-21 +### Added +- Added support (still in Beta) for Chrome's Web Serial API on ChromeOS. + Other operating systems should not be affected. diff --git a/LICENSE.txt b/LICENSE.txt index dab1f246..2a9bcdab 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,7 +1,7 @@ This file includes licensing information for arduino-create-agent-js-client. Copyright (c) 2018 -Authors: Alberto Iannaccone, Stefania Mellai, Gabriele Destefanis +Authors: Alberto Iannaccone, Stefania Mellai, Gabriele Destefanis, Christian Sarnataro The software is released under the GNU General Public License, which covers the main body of the arduino-create-agent-js-client code. The terms of this license can be found at: diff --git a/README.md b/README.md index fa470405..3f03870d 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,10 @@ [![npm version](https://badge.fury.io/js/arduino-create-agent-js-client.svg)](https://badge.fury.io/js/arduino-create-agent-js-client) # arduino-create-agent-js-client -JS module providing discovery of the [Arduino Create Plugin](https://github.com/arduino/arduino-create-agent) and communication with it +JS module providing discovery of the [Arduino Create Agent](https://github.com/arduino/arduino-create-agent) and communication with it + +## Changelog +See [CHANGELOG.md](https://github.com/arduino/arduino-create-agent-js-client/blob/HEAD/CHANGELOG.md) for more details. ## Installation @@ -38,11 +41,22 @@ daemon.devicesList.subscribe(({serial, network}) => { // Open serial monitor daemon.openSerialMonitor('port-name'); -// Read from serial monitor +// Read from serial monitor (ouputs string) daemon.serialMonitorMessages.subscribe(message => { console.log(message); }); +// Read from serial monitor, output object with source port name +/* + { + "P": "dev/ttyACM0", + "D":"output text here\r\n" + } +*/ +daemon.serialMonitorMessagesWithPort.subscribe(messageObj => { + console.log(messageObj); +}); + // Write to serial monitor daemon.writeSerial('port-name', 'message'); @@ -70,6 +84,34 @@ daemon.downloading.subscribe(download => { ``` +## Version 2 + +Version 2 of the arduino-create-agent aims to provide a cleaner api based on promises. +It will remain confined to a v2 property on the daemon object until it will be stable. +At the moment it only supports tool management. + +```js +daemon.agentV2Found.subscribe(daemonV2 => { + if (!daemonV2) { + // Your Agent doesn't support v2 + } + // Your Agent supports v2 +}); + +daemon.v2.installedTools() + .then(tools => console.debug(tools)) // [{"name":"avrdude","version":"6.3.0-arduino9","packager":"arduino"}] + +let payload = { + name: 'avrdude', + version: '6.3.0-arduino9', + packager: 'arduino', + url: 'http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino9-i686-w64-mingw32.zip', + checksum: 'SHA-256:f3c5cfa8d0b3b0caee81c5b35fb6acff89c342ef609bf4266734c6266a256d4f', + signature: '7628b488c7ffd21ae1ca657245751a4043c419fbab5c256a020fb53f17eb88686439f54f18e78a80b40fc2de742f79b78ed4338c959216dc8ae8279e482d2d4117eeaf34a281ce2369d1dc4356f782c0940d82610f1c892e913b637391c39e95d4d4dfe82d8dbc5350b833186a70a62c7952917481bad798a9c8b4905df91bd914fbdfd6e98ef75c8f7fb06284278da449ce05b27741d6eda156bbdb906d519ff7d7d5042379fdfc55962b3777fb9240b368552182758c297e39c72943d75d177f2dbb584b2210301250796dbe8af11f0cf06d762fe4f912294f4cdc8aff26715354cfb33010a81342fbbc438912eb424a39fc0c52a9b2bf722051a6f3b024bd' +} +daemon.v2.installTool(payload) // Will install the tool in the system +``` + ## Development and test features Just run `npm run dev` and open your browser on http://localhost:8000 @@ -79,5 +121,6 @@ To enable communication between your [local installation](http://localhost:8000/ add `origins = http://localhost:8000` on your agent config.ini file (if you are using https, add `origins = https://localhost:8000`). -- On macOs ~/Applications/ArduinoCreateAgent-1.1/ArduinoCreateAgent.app/Contents/MacOS/config.ini -- On Linux ~/ArduinoCreateAgent-1.1/config.ini +- On macOs ~/Applications/ArduinoCreateAgent/ArduinoCreateAgent.app/Contents/MacOS/config.ini +- On Linux ~/ArduinoCreateAgent/config.ini +- On Windows C:\Users\\[your user]\AppData\Roaming\ArduinoCreateAgent diff --git a/demo/.babelrc b/demo/.babelrc index 4ffef06d..30d8a8ba 100644 --- a/demo/.babelrc +++ b/demo/.babelrc @@ -1,3 +1,6 @@ { - "presets": ["env", "react"] -} + "presets": [ + "@babel/env", + "@babel/react" + ] +} \ No newline at end of file diff --git a/demo/app.jsx b/demo/app.jsx index 0eb80b3f..1de7aa89 100644 --- a/demo/app.jsx +++ b/demo/app.jsx @@ -20,11 +20,15 @@ import React from 'react'; import Daemon from '../src'; +import FirmwareUpdater from '../src/firmware-updater'; import { HEX } from './serial_mirror'; +import V2 from './v2/v2.jsx'; const chromeExtensionID = 'hfejhkbipnickajaidoppbadcomekkde'; +const isChromeOs = () => window.navigator.userAgent.indexOf(' CrOS ') !== -1; + const scrollToBottom = (target) => { if (target) { target.scrollTop = target.scrollHeight; // eslint-disable-line no-param-reassign @@ -32,16 +36,27 @@ const scrollToBottom = (target) => { }; const daemon = new Daemon('https://builder.arduino.cc/v3/boards', chromeExtensionID); +const firmwareUpdater = new FirmwareUpdater(daemon); -const handleUpload = () => { - const target = { - board: 'arduino:samd:mkr1000', - port: '/dev/ttyACM0', - network: false - }; +const handleBootloaderMode = (e, port) => { + e.preventDefault(); + daemon.setBootloaderMode(port); +}; - // Upload a compiled sketch. - daemon.uploadSerial(target, 'serial_mirror', { bin: HEX }); +const handleUpdateFirmware = (e, boardId, port, firmwareVersion) => { + e.preventDefault(); + if (![firmwareUpdater.updateStatusEnum.NOPE, firmwareUpdater.updateStatusEnum.DONE, firmwareUpdater.updateStatusEnum.ERROR].includes(firmwareUpdater.updating.getValue().status)) { + return; + } + firmwareUpdater.updateFirmware(boardId, port, firmwareVersion); + firmwareUpdater.updatingDone.subscribe(() => { + console.log('Firmware updated successfully!'); + }); + + firmwareUpdater.updatingError.subscribe(update => { + console.log('Something went wrong when trying to update the firmware'); + console.error(update.err); + }); }; const handleDownloadTool = e => { @@ -73,13 +88,17 @@ class App extends React.Component { uploadError: '', downloadStatus: '', downloadError: '', - supportedBoards: [] + serialInput: '', + supportedBoards: [], + uploadingPort: '' }; this.handleOpen = this.handleOpen.bind(this); this.handleClose = this.handleClose.bind(this); this.handleSend = this.handleSend.bind(this); + this.handleChangeSerial = this.handleChangeSerial.bind(this); this.showError = this.showError.bind(this); this.clearError = this.clearError.bind(this); + this.handleUpload = this.handleUpload.bind(this); } componentDidMount() { @@ -95,6 +114,9 @@ class App extends React.Component { }); daemon.error.subscribe(this.showError); + daemon.serialMonitorError.subscribe(this.showError); + daemon.uploadingError.subscribe(this.showError); + daemon.downloadingError.subscribe(this.showError); daemon.devicesList.subscribe(({ serial, network }) => this.setState({ serialDevices: serial, @@ -114,6 +136,10 @@ class App extends React.Component { scrollToBottom(serialTextarea); }); + daemon.serialMonitorMessagesWithPort.subscribe(messageObj => { + console.log(messageObj); + }); + daemon.uploading.subscribe(upload => { this.setState({ uploadStatus: upload.status, uploadError: upload.err }); // console.log(upload); @@ -127,8 +153,20 @@ class App extends React.Component { } } + requestDevicePermission = () => { + if ('serial' in navigator) { + navigator.serial.requestPort([{ usbVendorId: 0x2341 }]).then((port) => { + daemon.devicesList.next({ + serial: [port], + network: [] + }); + }); + } + }; + showError(err) { this.setState({ error: err }); + scrollToBottom(document.body); } clearError() { @@ -148,36 +186,61 @@ class App extends React.Component { this.setState({ serialPortOpen: null }); } + handleChangeSerial(e) { + this.setState({ serialInput: e.target.value }); + } + handleSend(e) { e.preventDefault(); const serialInput = document.getElementById('serial-input'); - const sendData = `${serialInput.value}\n`; + const sendData = `${this.state.serialInput}\n`; daemon.writeSerial(this.state.serialPortOpen, sendData); serialInput.focus(); - serialInput.value = ''; + this.setState({ serialInput: '' }); + } + + handleUpload() { + const target = { + board: 'arduino:samd:mkr1000', + port: '/dev/ttyACM1', + network: false + }; + + this.setState({ uploadingPort: target.port }); + daemon.boardPortAfterUpload.subscribe(portStatus => { + if (portStatus.hasChanged) { + this.setState({ uploadingPort: portStatus.newPort }); + } + }); + + // Upload a compiled sketch. + daemon.uploadSerial(target, 'serial_mirror', { bin: HEX }); } render() { - const listSerialDevices = this.state.serialDevices.map((device, i) => -
  • - {device.Name} - IsOpen: - {device.IsOpen ? 'true' : 'false'} - - this.handleOpen(e, device.Name)}> + const listSerialDevices = this.state.serialDevices.map((device, i) =>
  • + {device.Name} - IsOpen: + {device.IsOpen ? 'true' : 'false'} + - this.handleOpen(e, device.Name)}> open - - this.handleClose(e, device.Name)}> + - this.handleClose(e, device.Name)}> close - -
  • ); - - const listNetworkDevices = this.state.networkDevices.map((device, i) => -
  • - {device.Name} -
  • ); - - const supportedBoards = this.state.supportedBoards.map((board, i) => -
  • - { board } -
  • ); + - handleBootloaderMode(e, device.Name)}> + bootloader mode + - handleUpdateFirmware(e, 'mkrwifi1010', device.Name, '1.2.1')}> + MKR WiFi 1010 update firmware + - handleUpdateFirmware(e, 'mkr1000', device.Name, '19.4.4')}> + MKR1000 update firmware + + ); + + const listNetworkDevices = this.state.networkDevices.map((device, i) =>
  • + {device.Name} +
  • ); + + const supportedBoards = this.state.supportedBoards.map((board, i) =>
  • + { board } +
  • ); let uploadClass; if (this.state.uploadStatus === daemon.UPLOAD_DONE) { @@ -225,8 +288,10 @@ class App extends React.Component {
    -

    Connected Devices

    - +
    +

    Connected Devices

    + { isChromeOs() && } +
    serial:
    { - this.state.supportedBoards.length ? -
    + this.state.supportedBoards.length + ?

    Supported boards