JS: Flipboard
This commit is contained in:
44
js/flipboard/scripts/a_runner.js
Normal file
44
js/flipboard/scripts/a_runner.js
Normal file
@@ -0,0 +1,44 @@
|
||||
let __dirpath = "/ext/apps/Scripts/flipboard";
|
||||
|
||||
let loader = load(__dirpath + "/loader_api.js");
|
||||
|
||||
// Prompt user for the .fb file to load, this is a FlipBoard script file
|
||||
let dialog = loader.require("dialog");
|
||||
let fb = dialog.pickFile(__dirpath, "fb.js");
|
||||
if (fb === undefined) {
|
||||
die("No file selected");
|
||||
}
|
||||
loader.load("fn", fb);
|
||||
print("Loaded", loader.fn.title);
|
||||
|
||||
// Initialize textbox
|
||||
let textbox = loader.require("textbox");
|
||||
textbox.setConfig("end", "text");
|
||||
textbox.emptyText();
|
||||
|
||||
// Initialize access to the Flipboard buttons
|
||||
let flipboardButton = loader.load("flipboardButton", __dirpath + "/fb_button_api.js");
|
||||
flipboardButton.init();
|
||||
|
||||
// Initialize access to the Flipboard addressable LEDs
|
||||
let color = loader.load("color", __dirpath + "/color_api.js");
|
||||
let flipboardLeds = loader.load("flipboardLeds", __dirpath + "/fb_leds_api.js");
|
||||
flipboardLeds.init(color, [color.green, color.red, color.yellow, color.blue]);
|
||||
|
||||
// Initialize the function callback
|
||||
loader.fn.init(loader);
|
||||
|
||||
// Main loop
|
||||
let buttonNumber = 0;
|
||||
while (true) {
|
||||
// Wait for a button press
|
||||
buttonNumber = flipboardButton.debounceButton(buttonNumber);
|
||||
|
||||
// Convert the button press to an array of LEDs to light up
|
||||
let pressedArray = flipboardButton.buttonNumberToArray(buttonNumber);
|
||||
|
||||
// Update the LED brightness.
|
||||
flipboardLeds.updateLeds(pressedArray);
|
||||
|
||||
loader.fn.buttonPressed(buttonNumber, pressedArray);
|
||||
}
|
||||
56
js/flipboard/scripts/badusb_textbox_fb.js
Normal file
56
js/flipboard/scripts/badusb_textbox_fb.js
Normal file
@@ -0,0 +1,56 @@
|
||||
({
|
||||
title: "BadUSB Textbox",
|
||||
primaryAction: function (buttonNumber) {
|
||||
// Do an action based on the button number.
|
||||
if (buttonNumber === 1) {
|
||||
this.api.badusb.println("https://www.youtube.com/@MrDerekJamison/playlists", 10);
|
||||
} else if (buttonNumber === 2) {
|
||||
this.api.badusb.print("Flipper Name: ", 10);
|
||||
this.api.require("flipper");
|
||||
this.api.badusb.println(this.api.flipper.getName(), 10);
|
||||
} else if (buttonNumber === 4) {
|
||||
this.api.badusb.println("I TYPE SLOW!", 250);
|
||||
} else if (buttonNumber === 8) {
|
||||
this.api.badusb.altPrintln("This was printed with Alt+Numpad method!");
|
||||
}
|
||||
},
|
||||
init: function (api) {
|
||||
this.api = api;
|
||||
|
||||
// Initialize access to the Flipper Zero speaker
|
||||
this.api.initSpeaker();
|
||||
|
||||
// Initialize access to the BadUSB (virtual keyboard) device
|
||||
this.api.initBadusb("/ext/badusb/assets/layouts/en-US.kl");
|
||||
|
||||
this.initTextbox();
|
||||
},
|
||||
initTextbox: function () {
|
||||
this.api.textbox.addText(this.title + "\n");
|
||||
this.api.textbox.addText("Press a button.\n");
|
||||
this.api.textbox.show();
|
||||
},
|
||||
updateTextbox: function (buttonNumber, pressedArray) {
|
||||
let text = "Button " + to_string(buttonNumber) + " pressed: ";
|
||||
for (let i = 0; i < 4; i++) {
|
||||
text += pressedArray[i] ? "X" : "_";
|
||||
}
|
||||
text += "\n";
|
||||
this.api.textbox.addText(text);
|
||||
},
|
||||
buttonPressed: function (buttonNumber, pressedArray) {
|
||||
// Redraw the textbox to show the button press.
|
||||
this.updateTextbox(buttonNumber, pressedArray);
|
||||
|
||||
// A button press of 0 means the user released all of the buttons.
|
||||
if (buttonNumber === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Play a tone for 100ms when button pressed.
|
||||
this.api.speaker.play(440 + 100 * buttonNumber, 1.00, 100);
|
||||
|
||||
// Perform the primary action.
|
||||
this.primaryAction(buttonNumber);
|
||||
}
|
||||
})
|
||||
96
js/flipboard/scripts/badusb_widget_fb.js
Normal file
96
js/flipboard/scripts/badusb_widget_fb.js
Normal file
@@ -0,0 +1,96 @@
|
||||
({
|
||||
title: "BadUSB Widget",
|
||||
primaryAction: function (buttonNumber) {
|
||||
// Do an action based on the button number.
|
||||
if (buttonNumber === 1) {
|
||||
this.api.badusb.hold("CTRL", "ALT");
|
||||
this.api.badusb.press("DELETE");
|
||||
this.api.badusb.release("CTRL", "ALT");
|
||||
delay(1000);
|
||||
this.api.badusb.press("DOWN");
|
||||
this.api.badusb.press("DOWN");
|
||||
delay(2000);
|
||||
this.api.badusb.press("DOWN");
|
||||
this.api.badusb.press("ENTER");
|
||||
} else if (buttonNumber === 2) {
|
||||
// Trigger sticky keys by pressing shift 5 times.
|
||||
for (let i = 0; i < 5; i++) {
|
||||
this.api.badusb.press("SHIFT");
|
||||
}
|
||||
} else if (buttonNumber === 4) {
|
||||
this.api.badusb.println("SYMBOL TEST `!@#$%^&*()_+-=[]{};'\\:\"|,./<>?", 10);
|
||||
} else if (buttonNumber === 8) {
|
||||
this.api.badusb.altPrintln("Alt+Numpad `!@#$%^&*()_+-=[]{};'\\:\"|,./<>?");
|
||||
}
|
||||
},
|
||||
init: function (api) {
|
||||
this.api = api;
|
||||
|
||||
this.splashScreen();
|
||||
|
||||
// Initialize access to the Flipper Zero speaker
|
||||
this.api.initSpeaker();
|
||||
|
||||
// Initialize access to the BadUSB (virtual keyboard) device
|
||||
this.api.initBadusb("prompt");
|
||||
|
||||
this.initWidget();
|
||||
},
|
||||
splashScreen: function () {
|
||||
this.api.require("widget");
|
||||
this.api.widget.show();
|
||||
let fxbmFlippy = this.api.widget.loadImageXbm(__dirpath + "/flippy.fxbm");
|
||||
let splash = [];
|
||||
splash.push(this.api.widget.addXbm(0, 0, fxbmFlippy));
|
||||
splash.push(this.api.widget.addText(70, 10, "Secondary", "Be sure"));
|
||||
splash.push(this.api.widget.addText(70, 20, "Secondary", "to attach"));
|
||||
splash.push(this.api.widget.addText(70, 30, "Secondary", "FlipBoard."));
|
||||
splash.push(this.api.widget.addText(70, 44, "Secondary", "Connect USB"));
|
||||
splash.push(this.api.widget.addText(70, 54, "Secondary", "data cable"));
|
||||
splash.push(this.api.widget.addText(70, 64, "Secondary", "to PC."));
|
||||
delay(5000);
|
||||
for (let i = 0; i < splash.length; i++) {
|
||||
this.api.widget.remove(splash[i]);
|
||||
}
|
||||
},
|
||||
initWidget: function () {
|
||||
this.api.widget.addText(25, 15, "Primary", this.title);
|
||||
this.status = this.api.widget.addText(10, 60, "Secondary", "Press a button!");
|
||||
|
||||
this.fxbmUp = this.api.widget.loadImageXbm(__dirpath + "/up.fxbm");
|
||||
this.fxbmDown = this.api.widget.loadImageXbm(__dirpath + "/down.fxbm");
|
||||
this.icons = [];
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
this.icons.push(this.api.widget.addXbm(9 + i * 30, 32, this.fxbmUp));
|
||||
this.api.widget.addCircle(10 + i * 30 + 3, 36, 10);
|
||||
}
|
||||
},
|
||||
updateWidget: function (buttonNumber, pressedArray) {
|
||||
for (let i = 0; i < 4; i++) {
|
||||
this.api.widget.remove(this.icons[i]);
|
||||
if (pressedArray[i]) {
|
||||
this.icons[i] = this.api.widget.addXbm(9 + i * 30, 32, this.fxbmDown);
|
||||
} else {
|
||||
this.icons[i] = this.api.widget.addXbm(9 + i * 30, 32, this.fxbmUp);
|
||||
}
|
||||
}
|
||||
this.api.widget.remove(this.status);
|
||||
this.status = this.api.widget.addText(10, 60, "Secondary", "Button " + to_string(buttonNumber));
|
||||
},
|
||||
buttonPressed: function (buttonNumber, pressedArray) {
|
||||
// Redraw the widget to show the button press.
|
||||
this.updateWidget(buttonNumber, pressedArray);
|
||||
|
||||
// A button press of 0 means the user released all of the buttons.
|
||||
if (buttonNumber === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Play a tone for 100ms when button pressed.
|
||||
this.api.speaker.play(440 + 100 * buttonNumber, 1.00, 100);
|
||||
|
||||
// Perform the primary action.
|
||||
this.primaryAction(buttonNumber);
|
||||
}
|
||||
})
|
||||
15
js/flipboard/scripts/color_api.js
Normal file
15
js/flipboard/scripts/color_api.js
Normal file
@@ -0,0 +1,15 @@
|
||||
({
|
||||
green: { red: 0x00, green: 0xFF, blue: 0x00 },
|
||||
red: { red: 0xFF, green: 0x00, blue: 0x00 },
|
||||
yellow: { red: 0xFF, green: 0x7F, blue: 0x00 },
|
||||
blue: { red: 0x00, green: 0x00, blue: 0xFF },
|
||||
default_glow: 0.10,
|
||||
bright_glow: 0.90,
|
||||
brightness: function (color, brightness) {
|
||||
return {
|
||||
red: color.red * brightness,
|
||||
green: color.green * brightness,
|
||||
blue: color.blue * brightness
|
||||
};
|
||||
}
|
||||
})
|
||||
BIN
js/flipboard/scripts/down.fxbm
Normal file
BIN
js/flipboard/scripts/down.fxbm
Normal file
Binary file not shown.
55
js/flipboard/scripts/fb_button_api.js
Normal file
55
js/flipboard/scripts/fb_button_api.js
Normal file
@@ -0,0 +1,55 @@
|
||||
({
|
||||
gpio: require("gpio"),
|
||||
button_pins: ["PB2", "PB3", "PA4", "PA6"],
|
||||
repeat: false,
|
||||
init: function () {
|
||||
for (let i = 0; i < this.button_pins.length; i++) {
|
||||
this.gpio.init(this.button_pins[i], "input", "up");
|
||||
}
|
||||
},
|
||||
getButtons: function () {
|
||||
let n = 0;
|
||||
for (let i = 0; i < this.button_pins.length; i++) {
|
||||
let isPressed = !this.gpio.read(this.button_pins[i]);
|
||||
n += isPressed ? 1 << i : 0;
|
||||
}
|
||||
|
||||
return n;
|
||||
},
|
||||
debounceButton: function (button) {
|
||||
let threshold = 3;
|
||||
let repeatThreshold = 5;
|
||||
let debounce = { counter: threshold, button: button & ~16 };
|
||||
while (true) {
|
||||
let button = this.getButtons();
|
||||
if (button !== debounce.button) {
|
||||
debounce.counter = 0;
|
||||
debounce.button = button;
|
||||
continue;
|
||||
} else {
|
||||
debounce.counter++;
|
||||
if (debounce.counter === threshold) {
|
||||
break;
|
||||
} else if (debounce.counter < threshold) {
|
||||
continue;
|
||||
} else if (debounce.counter > threshold) {
|
||||
if (this.repeat && debounce.counter > repeatThreshold) {
|
||||
debounce.button |= 16;
|
||||
break;
|
||||
}
|
||||
delay(1);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return debounce.button;
|
||||
},
|
||||
buttonNumberToArray: function (button_number) {
|
||||
let binary_array = [];
|
||||
for (let i = 0; i < this.button_pins.length; i++) {
|
||||
binary_array.push((button_number >> i) & 1);
|
||||
}
|
||||
return binary_array;
|
||||
}
|
||||
})
|
||||
28
js/flipboard/scripts/fb_leds_api.js
Normal file
28
js/flipboard/scripts/fb_leds_api.js
Normal file
@@ -0,0 +1,28 @@
|
||||
({
|
||||
color_api: undefined,
|
||||
rgbLeds: require("rgbleds"),
|
||||
led_colors: [],
|
||||
updateLeds: function (bright) {
|
||||
let isChanged = false;
|
||||
for (let i = 0; i < this.led_colors.length; i++) {
|
||||
let b = bright[i] ? this.color_api.bright_glow : this.color_api.default_glow;
|
||||
let c = this.color_api.brightness(this.led_colors[i], b); // using global 'color' object.
|
||||
if (this.rgbLeds.set(i, c) !== c) {
|
||||
isChanged = true;
|
||||
}
|
||||
}
|
||||
// We always call update, so LEDs can be unplugged and reconnected.
|
||||
this.rgbLeds.update();
|
||||
return isChanged;
|
||||
},
|
||||
init: function (color_api, colors) {
|
||||
this.color_api = color_api;
|
||||
this.led_colors = colors;
|
||||
this.rgbLeds.setup({ "pin": "PC3", "count": this.led_colors.length, "spec": "WS2812B" });
|
||||
let state = [];
|
||||
for (let i = 0; i < this.led_colors.length; i++) {
|
||||
state.push(false);
|
||||
}
|
||||
this.updateLeds(state);
|
||||
}
|
||||
})
|
||||
BIN
js/flipboard/scripts/flippy.fxbm
Normal file
BIN
js/flipboard/scripts/flippy.fxbm
Normal file
Binary file not shown.
66
js/flipboard/scripts/infrared_fb.js
Normal file
66
js/flipboard/scripts/infrared_fb.js
Normal file
@@ -0,0 +1,66 @@
|
||||
({
|
||||
title: "Infrared Blast",
|
||||
primaryAction: function (buttonNumber) {
|
||||
// Do an action based on the button number.
|
||||
if (buttonNumber === 1) {
|
||||
this.api.textbox.addText("\nPower");
|
||||
this.api.infrared.sendProtocol("Samsung32", 0x07, 0x02);
|
||||
} else if (buttonNumber === 2) {
|
||||
this.api.textbox.addText("\nVolume Up");
|
||||
this.api.infrared.sendProtocol("Samsung32", 0x07, 0x07);
|
||||
} else if (buttonNumber === 4) {
|
||||
this.api.textbox.addText("\nVolume Down");
|
||||
this.api.infrared.sendProtocol("Samsung32", 0x07, 0x0B);
|
||||
} else if (buttonNumber === (2 | 8)) {
|
||||
this.api.textbox.addText("\nChannel Up");
|
||||
this.api.infrared.sendProtocol("Samsung32", 0x07, 0x12);
|
||||
} else if (buttonNumber === (4 | 8)) {
|
||||
this.api.textbox.addText("\nChannel Down");
|
||||
this.api.infrared.sendProtocol("Samsung32", 0x07, 0x10);
|
||||
}
|
||||
},
|
||||
init: function (api) {
|
||||
this.api = api;
|
||||
|
||||
// Allow the Flipboard button to repeat action [adding button 16] when held down.
|
||||
this.api.flipboardButton.repeat = true;
|
||||
|
||||
// Initialize access to the Flipper Zero speaker
|
||||
this.api.initSpeaker();
|
||||
|
||||
// Initialize access to the Infrared module
|
||||
this.api.require("infrared");
|
||||
|
||||
this.initTextbox();
|
||||
},
|
||||
initTextbox: function () {
|
||||
this.api.textbox.addText(this.title + "\n");
|
||||
this.api.textbox.addText("Green: Power.\nRed = Volume +\nYellow = Volume -\nBlue + Red = Channel +\nBlue + Yellow = Channel -");
|
||||
this.api.textbox.show();
|
||||
},
|
||||
buttonPressed: function (buttonNumber, _pressedArray) {
|
||||
// Ignore button 1 [Power] if repeat (16).
|
||||
if (buttonNumber === (1 | 16)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove the repeat flag.
|
||||
buttonNumber = buttonNumber & ~16;
|
||||
|
||||
// A button press of 0 means the user released all of the buttons.
|
||||
if (buttonNumber === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore holding button 8
|
||||
if (buttonNumber === 8) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Play a tone for 100ms when button pressed.
|
||||
this.api.speaker.play(440 + 100 * buttonNumber, 1.00, 100);
|
||||
|
||||
// Perform the primary action.
|
||||
this.primaryAction(buttonNumber);
|
||||
}
|
||||
})
|
||||
67
js/flipboard/scripts/loader_api.js
Normal file
67
js/flipboard/scripts/loader_api.js
Normal file
@@ -0,0 +1,67 @@
|
||||
({
|
||||
isDefined: function (name) {
|
||||
return this[name] !== undefined;
|
||||
},
|
||||
require: function (name) {
|
||||
let lib = undefined;
|
||||
if (!this.isDefined(name)) {
|
||||
lib = require(name);
|
||||
this[name] = lib;
|
||||
} else {
|
||||
lib = this[name];
|
||||
}
|
||||
return lib;
|
||||
},
|
||||
load: function (name, path) {
|
||||
let lib = undefined;
|
||||
if (!this.isDefined(name)) {
|
||||
lib = load(path);
|
||||
this[name] = lib;
|
||||
} else {
|
||||
lib = this[name];
|
||||
}
|
||||
return lib;
|
||||
},
|
||||
defaultBadusbLayout: "/ext/badusb/assets/layouts/en-US.kl",
|
||||
initBadusb: function (layout_path) {
|
||||
// Initialize access to the BadUSB (virtual keyboard) device
|
||||
if (!this.isDefined("badusb")) {
|
||||
this.require("badusb");
|
||||
if (layout_path === undefined) {
|
||||
layout_path = this.defaultBadusbLayout;
|
||||
} else if (layout_path.charCodeAt(0) !== 0x2F) { // If not an absolute path, prompt for file.
|
||||
if (!this.isDefined("dialog")) {
|
||||
this.require("dialog");
|
||||
}
|
||||
layout_path = this.dialog.pickFile("/ext/badusb/assets/layouts", ".kl");
|
||||
if (layout_path === undefined) {
|
||||
layout_path = this.defaultBadusbLayout;
|
||||
}
|
||||
}
|
||||
this.badusb.setup({
|
||||
vid: 0x05ac,
|
||||
pid: 0x021e,
|
||||
mfr_name: "Apple",
|
||||
prod_name: "Keyboard",
|
||||
layout_path: layout_path
|
||||
});
|
||||
}
|
||||
},
|
||||
initSpeaker: function () {
|
||||
// Initialize access to the speaker
|
||||
if (!this.isDefined("speaker")) {
|
||||
this.require("speaker");
|
||||
|
||||
this.speaker.acquire(1000); // NOTE: it will be released when the script exits.
|
||||
}
|
||||
},
|
||||
initSubghz: function () {
|
||||
// Initialize access to the Sub-GHz radio
|
||||
if (!this.isDefined("subghz")) {
|
||||
this.require("subghz");
|
||||
this.subghz.setup();
|
||||
// For some reason subghz impacts our GPIO pins (so reset them).
|
||||
this.flipboardButton.init();
|
||||
}
|
||||
}
|
||||
})
|
||||
41
js/flipboard/scripts/subghz_fb.js
Normal file
41
js/flipboard/scripts/subghz_fb.js
Normal file
@@ -0,0 +1,41 @@
|
||||
({
|
||||
title: "SubGHz Sender",
|
||||
primaryAction: function (buttonNumber) {
|
||||
// Do an action based on the button number.
|
||||
if (buttonNumber === 1) {
|
||||
this.api.speaker.start(540, 1.00);
|
||||
this.api.subghz.transmitFile("/ext/subghz/Light_on.sub");
|
||||
this.api.speaker.stop();
|
||||
} else if (buttonNumber === 2) {
|
||||
this.api.speaker.start(640, 1.00);
|
||||
this.api.subghz.transmitFile("/ext/subghz/Light_off.sub");
|
||||
this.api.speaker.stop();
|
||||
}
|
||||
},
|
||||
init: function (api) {
|
||||
this.api = api;
|
||||
|
||||
// Initialize access to the Flipper Zero speaker
|
||||
this.api.initSpeaker();
|
||||
|
||||
// Initialize access to the SubGHz module
|
||||
this.api.initSubghz();
|
||||
|
||||
this.initTextbox();
|
||||
},
|
||||
initTextbox: function () {
|
||||
this.api.textbox.addText(this.title + "\n");
|
||||
this.api.textbox.addText("Button 1 = Light on.\n");
|
||||
this.api.textbox.addText("Button 2 = Light off.\n");
|
||||
this.api.textbox.show();
|
||||
},
|
||||
buttonPressed: function (buttonNumber, pressedArray) {
|
||||
// We only use the first two buttons.
|
||||
if (buttonNumber !== 1 && buttonNumber !== 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Perform the primary action.
|
||||
this.primaryAction(buttonNumber);
|
||||
}
|
||||
})
|
||||
BIN
js/flipboard/scripts/up.fxbm
Normal file
BIN
js/flipboard/scripts/up.fxbm
Normal file
Binary file not shown.
Reference in New Issue
Block a user