mirror of
https://github.com/balena-io/etcher.git
synced 2025-07-25 12:16:37 +00:00
commit
8b5a42073d
4
Makefile
4
Makefile
@ -124,7 +124,7 @@ TARGETS = \
|
|||||||
help \
|
help \
|
||||||
info \
|
info \
|
||||||
lint \
|
lint \
|
||||||
lint-js \
|
lint-ts \
|
||||||
lint-sass \
|
lint-sass \
|
||||||
lint-cpp \
|
lint-cpp \
|
||||||
lint-spell \
|
lint-spell \
|
||||||
@ -149,7 +149,7 @@ sass:
|
|||||||
node-sass lib/gui/app/scss/main.scss > lib/gui/css/main.css
|
node-sass lib/gui/app/scss/main.scss > lib/gui/css/main.css
|
||||||
|
|
||||||
lint-ts:
|
lint-ts:
|
||||||
resin-lint --typescript typings lib tests scripts/clean-shrinkwrap.ts webpack.config.ts
|
resin-lint --fix --typescript typings lib tests scripts/clean-shrinkwrap.ts webpack.config.ts
|
||||||
|
|
||||||
lint-sass:
|
lint-sass:
|
||||||
sass-lint lib/gui/scss
|
sass-lint lib/gui/scss
|
||||||
|
@ -14,131 +14,17 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { delay } from 'bluebird';
|
import {
|
||||||
import { promises as fs } from 'fs';
|
AnimationFunction,
|
||||||
|
blinkWhite,
|
||||||
|
breatheGreen,
|
||||||
|
Color,
|
||||||
|
RGBLed,
|
||||||
|
} from 'sys-class-rgb-led';
|
||||||
|
|
||||||
import * as settings from './settings';
|
import * as settings from './settings';
|
||||||
import { observe } from './store';
|
import { observe } from './store';
|
||||||
|
|
||||||
class Led {
|
|
||||||
private handle: fs.FileHandle;
|
|
||||||
private lastValue?: number;
|
|
||||||
|
|
||||||
constructor(private path: string) {}
|
|
||||||
|
|
||||||
private async open() {
|
|
||||||
if (this.handle === undefined) {
|
|
||||||
this.handle = await fs.open(this.path, 'w');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async setIntensity(intensity: number) {
|
|
||||||
if (intensity < 0 || intensity > 1) {
|
|
||||||
throw new Error('Led intensity must be between 0 and 1');
|
|
||||||
}
|
|
||||||
const value = Math.round(intensity * 255);
|
|
||||||
if (value !== this.lastValue) {
|
|
||||||
await this.open();
|
|
||||||
await this.handle.write(value.toString(), 0);
|
|
||||||
// On a regular file we would also need to truncate to the written value length but it looks like it's not the case on sysfs files
|
|
||||||
this.lastValue = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Color = [number, number, number];
|
|
||||||
export type AnimationFunction = (t: number) => Color;
|
|
||||||
|
|
||||||
function delay1(duration: number): Promise<void> {
|
|
||||||
// delay that accepts Infinity
|
|
||||||
if (duration === Infinity) {
|
|
||||||
return new Promise(() => {
|
|
||||||
// Never resolve
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return delay(duration);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function cancellableDelay(
|
|
||||||
duration: number,
|
|
||||||
): { promise: Promise<void>; cancel: () => void } {
|
|
||||||
let maybeCancel: () => void;
|
|
||||||
const cancel = () => {
|
|
||||||
if (maybeCancel !== undefined) {
|
|
||||||
maybeCancel();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const cancelPromise: Promise<void> = new Promise(resolve => {
|
|
||||||
maybeCancel = resolve;
|
|
||||||
});
|
|
||||||
const promise = Promise.race([delay1(duration), cancelPromise]);
|
|
||||||
return { promise, cancel };
|
|
||||||
}
|
|
||||||
|
|
||||||
export class RGBLed {
|
|
||||||
private leds: [Led, Led, Led];
|
|
||||||
private animation: AnimationFunction;
|
|
||||||
private period: number; // in ms
|
|
||||||
private wakeUp = () => {
|
|
||||||
// noop until this.loop() is called
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(paths: [string, string, string]) {
|
|
||||||
this.leds = paths.map(path => new Led(path)) as [Led, Led, Led];
|
|
||||||
this.setStaticColor([0, 0, 0]);
|
|
||||||
this.loop();
|
|
||||||
}
|
|
||||||
|
|
||||||
private setFrequency(frequency: number) {
|
|
||||||
if (frequency < 0) {
|
|
||||||
throw new Error('frequency must be greater or equal to 0');
|
|
||||||
}
|
|
||||||
this.period = 1000 / frequency;
|
|
||||||
this.wakeUp();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async loop() {
|
|
||||||
while (true) {
|
|
||||||
const start = new Date().getTime();
|
|
||||||
await this.setColor(this.animation(start));
|
|
||||||
const end = new Date().getTime();
|
|
||||||
const duration = end - start;
|
|
||||||
const { promise, cancel } = cancellableDelay(this.period - duration);
|
|
||||||
this.wakeUp = cancel;
|
|
||||||
await promise;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public setAnimation(animation: AnimationFunction, frequency = 10) {
|
|
||||||
this.animation = animation;
|
|
||||||
this.setFrequency(frequency);
|
|
||||||
}
|
|
||||||
|
|
||||||
public setStaticColor(color: Color) {
|
|
||||||
this.setAnimation(() => color, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async setColor(color: Color) {
|
|
||||||
await Promise.all([
|
|
||||||
this.leds[0].setIntensity(color[0]),
|
|
||||||
this.leds[1].setIntensity(color[1]),
|
|
||||||
this.leds[2].setIntensity(color[2]),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Animations:
|
|
||||||
function breatheGreen(t: number): Color {
|
|
||||||
const intensity = (1 + Math.sin(t / 1000)) / 2;
|
|
||||||
return [0, intensity, 0];
|
|
||||||
}
|
|
||||||
|
|
||||||
function blinkWhite(t: number): Color {
|
|
||||||
const intensity = Math.floor(t / 1000) % 2;
|
|
||||||
return [intensity, intensity, intensity];
|
|
||||||
}
|
|
||||||
|
|
||||||
const leds: Map<string, RGBLed> = new Map();
|
const leds: Map<string, RGBLed> = new Map();
|
||||||
|
|
||||||
function setLeds(
|
function setLeds(
|
||||||
@ -184,16 +70,16 @@ export function init() {
|
|||||||
// ledsMapping is something like:
|
// ledsMapping is something like:
|
||||||
// {
|
// {
|
||||||
// 'platform-xhci-hcd.0.auto-usb-0:1.1.1:1.0-scsi-0:0:0:0': [
|
// 'platform-xhci-hcd.0.auto-usb-0:1.1.1:1.0-scsi-0:0:0:0': [
|
||||||
// '/sys/class/leds/led1_r',
|
// 'led1_r',
|
||||||
// '/sys/class/leds/led1_g',
|
// 'led1_g',
|
||||||
// '/sys/class/leds/led1_b',
|
// 'led1_b',
|
||||||
// ],
|
// ],
|
||||||
// ...
|
// ...
|
||||||
// }
|
// }
|
||||||
const ledsMapping: _.Dictionary<[string, string, string]> =
|
const ledsMapping: _.Dictionary<[string, string, string]> =
|
||||||
settings.get('ledsMapping') || {};
|
settings.get('ledsMapping') || {};
|
||||||
for (const [drivePath, ledsPaths] of Object.entries(ledsMapping)) {
|
for (const [drivePath, ledsNames] of Object.entries(ledsMapping)) {
|
||||||
leds.set('/dev/disk/by-path/' + drivePath, new RGBLed(ledsPaths));
|
leds.set('/dev/disk/by-path/' + drivePath, new RGBLed(ledsNames));
|
||||||
}
|
}
|
||||||
observe(state => {
|
observe(state => {
|
||||||
const availableDrives = state
|
const availableDrives = state
|
||||||
|
@ -227,12 +227,15 @@ export function performWrite(
|
|||||||
!flashResults.cancelled &&
|
!flashResults.cancelled &&
|
||||||
!_.get(flashResults, ['results', 'bytesWritten'])
|
!_.get(flashResults, ['results', 'bytesWritten'])
|
||||||
) {
|
) {
|
||||||
throw errors.createUserError({
|
reject(
|
||||||
title: 'The writer process ended unexpectedly',
|
errors.createUserError({
|
||||||
description:
|
title: 'The writer process ended unexpectedly',
|
||||||
'Please try again, and contact the Etcher team if the problem persists',
|
description:
|
||||||
code: 'ECHILDDIED',
|
'Please try again, and contact the Etcher team if the problem persists',
|
||||||
});
|
code: 'ECHILDDIED',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
resolve(flashResults);
|
resolve(flashResults);
|
||||||
});
|
});
|
||||||
|
@ -71,7 +71,7 @@ const getErrorMessageFromCode = (errorCode: string) => {
|
|||||||
return '';
|
return '';
|
||||||
};
|
};
|
||||||
|
|
||||||
const flashImageToDrive = async (goToSuccess: () => void) => {
|
async function flashImageToDrive(goToSuccess: () => void): Promise<string> {
|
||||||
const devices = selection.getSelectedDevices();
|
const devices = selection.getSelectedDevices();
|
||||||
const image: any = selection.getImage();
|
const image: any = selection.getImage();
|
||||||
const drives = _.filter(availableDrives.getDrives(), (drive: any) => {
|
const drives = _.filter(availableDrives.getDrives(), (drive: any) => {
|
||||||
@ -129,7 +129,7 @@ const flashImageToDrive = async (goToSuccess: () => void) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return '';
|
return '';
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary Get progress button label
|
* @summary Get progress button label
|
||||||
|
@ -21,7 +21,9 @@
|
|||||||
// if passing `ELECTRON_RUN_AS_NODE`, you have to pass the path to the asar
|
// if passing `ELECTRON_RUN_AS_NODE`, you have to pass the path to the asar
|
||||||
// or the entry point file (this file) manually as an argument.
|
// or the entry point file (this file) manually as an argument.
|
||||||
|
|
||||||
if (process.env.ELECTRON_RUN_AS_NODE) {
|
import { env } from 'process';
|
||||||
|
|
||||||
|
if (env.ELECTRON_RUN_AS_NODE) {
|
||||||
import('./gui/modules/child-writer');
|
import('./gui/modules/child-writer');
|
||||||
} else {
|
} else {
|
||||||
import('./gui/etcher');
|
import('./gui/etcher');
|
||||||
|
7
npm-shrinkwrap.json
generated
7
npm-shrinkwrap.json
generated
@ -12694,6 +12694,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
|
||||||
"integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ=="
|
"integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ=="
|
||||||
},
|
},
|
||||||
|
"sys-class-rgb-led": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/sys-class-rgb-led/-/sys-class-rgb-led-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-ckjrMCWWwg1J4d+B3xlTPLhgK6U/2qpW1TYzCLBSULKoLk2tFchis7nDEOmcbiLfpR/8GQK1Q2CrDNleF0USHA=="
|
||||||
|
},
|
||||||
"tapable": {
|
"tapable": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
|
||||||
@ -14457,4 +14462,4 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -20,7 +20,6 @@
|
|||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "make lint test sanity-checks",
|
"test": "make lint test sanity-checks",
|
||||||
"prettier": "prettier --config ./node_modules/resin-lint/config/.prettierrc --write \"lib/**/*.ts\" \"lib/**/*.tsx\" \"tests/**/*.ts\" \"webpack.config.ts\" \"scripts/clean-shrinkwrap.ts\"",
|
|
||||||
"start": "./node_modules/.bin/electron .",
|
"start": "./node_modules/.bin/electron .",
|
||||||
"postshrinkwrap": "ts-node ./scripts/clean-shrinkwrap.ts",
|
"postshrinkwrap": "ts-node ./scripts/clean-shrinkwrap.ts",
|
||||||
"configure": "node-gyp configure",
|
"configure": "node-gyp configure",
|
||||||
@ -39,7 +38,6 @@
|
|||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"./**/*.{ts,tsx}": [
|
"./**/*.{ts,tsx}": [
|
||||||
"npm run prettier",
|
|
||||||
"make lint-ts",
|
"make lint-ts",
|
||||||
"git add"
|
"git add"
|
||||||
]
|
]
|
||||||
@ -84,6 +82,7 @@
|
|||||||
"styled-components": "^4.2.0",
|
"styled-components": "^4.2.0",
|
||||||
"styled-system": "^4.1.0",
|
"styled-system": "^4.1.0",
|
||||||
"sudo-prompt": "^9.0.0",
|
"sudo-prompt": "^9.0.0",
|
||||||
|
"sys-class-rgb-led": "^2.1.0",
|
||||||
"tmp": "^0.1.0",
|
"tmp": "^0.1.0",
|
||||||
"uuid": "^3.0.1"
|
"uuid": "^3.0.1"
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user