sys-class-rgb-led
Version:
Animates /sys/class/leds rgb leds
140 lines • 4.5 kB
JavaScript
;
/*
* Copyright 2020 balena.io
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.blinkWhite = exports.rainbow = exports.breatheGreen = exports.sin01 = exports.Animator = exports.RGBLed = void 0;
const fs_1 = require("fs");
const path_1 = require("path");
const LEDS_DIR = '/sys/class/leds';
class Led {
constructor(name) {
this.name = name;
this.ready = this.open();
this.ready.catch(() => {
// Avoid unhandled rejections
});
}
async open() {
this.maxBrightness = parseInt(await fs_1.promises.readFile(path_1.join(LEDS_DIR, this.name, 'max_brightness'), 'utf8'), 10);
this.handle = await fs_1.promises.open(path_1.join(LEDS_DIR, this.name, 'brightness'), 'w');
}
async close() {
await this.ready;
await this.handle.close();
}
async setIntensity(intensity) {
await this.ready;
if (intensity < 0 || intensity > 1) {
throw new Error('Led intensity must be between 0 and 1');
}
const value = Math.round(intensity * this.maxBrightness);
if (value !== this.lastValue) {
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;
}
}
}
class RGBLed {
constructor(names) {
this.leds = names.map((name) => new Led(name));
}
async close() {
await Promise.all(this.leds.map((l) => l.close()));
}
async setColor(color) {
await Promise.all([
this.leds[0].setIntensity(color[0]),
this.leds[1].setIntensity(color[1]),
this.leds[2].setIntensity(color[2]),
]);
}
}
exports.RGBLed = RGBLed;
class Animator {
constructor(mapping, frequency = 10) {
this.mapping = mapping;
this.updating = false;
this.setFrequency(frequency);
}
async setFrequency(frequency) {
if (frequency < 0) {
throw new Error('frequency must be greater or equal to 0');
}
const period = 1000 / frequency;
this.stop();
if (period === Infinity) {
// frequency is 0
if (this.lastUpdate !== undefined) {
await this.lastUpdate;
}
}
else {
this.start(period);
}
}
start(period) {
if (this.intervalId === undefined) {
this.intervalId = setInterval(() => {
this.updateOrSkip();
}, period);
}
}
stop() {
if (this.intervalId !== undefined) {
clearInterval(this.intervalId);
this.intervalId = undefined;
}
}
updateOrSkip() {
if (!this.updating) {
this.lastUpdate = this.update();
}
}
async update() {
this.updating = true;
const t = new Date().getTime() / 1000;
const promises = [];
for (const { animation, rgbLeds } of this.mapping) {
const color = animation(t);
promises.push(...rgbLeds.map((rgbLed) => rgbLed.setColor(color)));
}
await Promise.all(promises);
this.updating = false;
}
}
exports.Animator = Animator;
function sin01(t) {
// sin(t) but the output is in the [0, 1] range
return (1 + Math.sin(t)) / 2;
}
exports.sin01 = sin01;
// Example animations:
function breatheGreen(t) {
return [0, sin01(t), 0];
}
exports.breatheGreen = breatheGreen;
const THIRD_OF_PI = Math.PI / 3;
function rainbow(t) {
return [sin01(t), sin01(t + 2 * THIRD_OF_PI), sin01(t + 4 * THIRD_OF_PI)];
}
exports.rainbow = rainbow;
function blinkWhite(t) {
const intensity = Math.floor(t) % 2;
return [intensity, intensity, intensity];
}
exports.blinkWhite = blinkWhite;
//# sourceMappingURL=index.js.map