Skip to content

Commit

Permalink
Merge pull request #14 from Spooks4576/dev
Browse files Browse the repository at this point in the history
JS: Add BLE Beacon, Keyboard, Math modules
  • Loading branch information
Willy-JL authored Mar 7, 2024
2 parents 423ff03 + eedeee4 commit 0c36944
Show file tree
Hide file tree
Showing 9 changed files with 867 additions and 3 deletions.
24 changes: 24 additions & 0 deletions applications/system/js_app/application.fam
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,27 @@ App(
requires=["js_app"],
sources=["modules/js_submenu.c"],
)

App(
appid="js_blebeacon",
apptype=FlipperAppType.PLUGIN,
entry_point="js_blebeacon_ep",
requires=["js_app"],
sources=["modules/js_blebeacon.c"],
)

App(
appid="js_math",
apptype=FlipperAppType.PLUGIN,
entry_point="js_math_ep",
requires=["js_app"],
sources=["modules/js_math.c"],
)

App(
appid="js_keyboard",
apptype=FlipperAppType.PLUGIN,
entry_point="js_keyboard_ep",
requires=["js_app"],
sources=["modules/js_keyboard.c"],
)
51 changes: 51 additions & 0 deletions applications/system/js_app/examples/apps/Scripts/blebeacon.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
let blebeacon = require("blebeacon");
let math = require("math");

let currentIndex = 0;
let watchValues = [
0x1A, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0A, 0x0B, 0x0C, 0x11, 0x12, 0x13, 0x14, 0x15,
0x16, 0x17, 0x18, 0xE4, 0xE5, 0x1B, 0x1C, 0x1D, 0x1E,
0x20, 0xEC, 0xEF
];

function generateRandomMac() {
let mac = [];
for (let i = 0; i < 6; i++) {
mac.push(math.floor(math.random() * 256));
}
return Uint8Array(mac);
}

function sendRandomModelAdvertisement() {
let model = watchValues[currentIndex];

let packet = [
14, 0xFF, 0x75, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x01, 0xFF, 0x00, 0x00, 0x43,
model
];

let intervalMs = 50;

// Power level, min interval and max interval are optional
blebeacon.setConfig(generateRandomMac(), 0x1F, intervalMs, intervalMs * 3);

blebeacon.setData(Uint8Array(packet));

blebeacon.start();

print("Sent data for model ID " + to_string(model));

currentIndex = (currentIndex + 1) % watchValues.length;

delay(intervalMs);

blebeacon.stop();
}

// Make sure it resets at script exit, true will keep advertising in background
blebeacon.keepAlive(true);

while (true) {
sendRandomModelAdvertisement();
}
19 changes: 19 additions & 0 deletions applications/system/js_app/examples/apps/Scripts/keyboard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
let keyboard = require("keyboard");

keyboard.setHeader("Example Text Input");

// Default text is optional
let text = keyboard.text(100, "Default text", true);
print("Got text:", text);

keyboard.setHeader("Example Byte Input");

// Default data is optional
let data = keyboard.byte(6, Uint8Array([1, 2, 3, 4, 5, 6]));
data = Uint8Array(data);
let result = "0x";
for (let i = 0; i < data.byteLength; i++) {
if (data[i] < 0x10) result += "0";
result += to_hex_string(data[i]);
}
print("Got data:", result);
47 changes: 47 additions & 0 deletions applications/system/js_app/examples/apps/Scripts/math.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
let math = require("math");

let absResult = math.abs(-5);
let acosResult = math.acos(0.5);
let acoshResult = math.acosh(2);
let asinResult = math.asin(0.5);
let asinhResult = math.asinh(2);
let atanResult = math.atan(1);
let atan2Result = math.atan2(1, 1);
let atanhResult = math.atanh(0.5);
let cbrtResult = math.cbrt(27);
let ceilResult = math.ceil(5.3);
let clz32Result = math.clz32(1);
let cosResult = math.cos(math.PI);
let expResult = math.exp(1);
let floorResult = math.floor(5.7);
let maxResult = math.max(3, 5);
let minResult = math.min(3, 5);
let powResult = math.pow(2, 3);
let randomResult = math.random();
let signResult = math.sign(-5);
let sinResult = math.sin(math.PI / 2);
let sqrtResult = math.sqrt(25);
let truncResult = math.trunc(5.7);

print("math.abs(-5):", absResult);
print("math.acos(0.5):", acosResult);
print("math.acosh(2):", acoshResult);
print("math.asin(0.5):", asinResult);
print("math.asinh(2):", asinhResult);
print("math.atan(1):", atanResult);
print("math.atan2(1, 1):", atan2Result);
print("math.atanh(0.5):", atanhResult);
print("math.cbrt(27):", cbrtResult);
print("math.ceil(5.3):", ceilResult);
print("math.clz32(1):", clz32Result);
print("math.cos(math.PI):", cosResult);
print("math.exp(1):", expResult);
print("math.floor(5.7):", floorResult);
print("math.max(3, 5):", maxResult);
print("math.min(3, 5):", minResult);
print("math.pow(2, 3):", powResult);
print("math.random():", randomResult);
print("math.sign(-5):", signResult);
print("math.sin(math.PI/2):", sinResult);
print("math.sqrt(25):", sqrtResult);
print("math.trunc(5.7):", truncResult);
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ submenu.setHeader("Select an option:");

let result = submenu.show();

print("Result: ", result);
print("Result:", result);
213 changes: 213 additions & 0 deletions applications/system/js_app/modules/js_blebeacon.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
#include "../js_modules.h"
#include <furi_hal_bt.h>
#include <extra_beacon.h>

typedef struct {
bool saved_prev_cfg;
bool prev_cfg_set;
GapExtraBeaconConfig prev_cfg;

bool saved_prev_data;
uint8_t prev_data[EXTRA_BEACON_MAX_DATA_SIZE];
uint8_t prev_data_len;

bool saved_prev_active;
bool prev_active;

bool keep_alive;
} JsBlebeaconInst;

static JsBlebeaconInst* get_this_ctx(struct mjs* mjs) {
mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);
JsBlebeaconInst* storage = mjs_get_ptr(mjs, obj_inst);
furi_assert(storage);
return storage;
}

static void ret_bad_args(struct mjs* mjs, const char* error) {
mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "%s", error);
mjs_return(mjs, MJS_UNDEFINED);
}

static bool check_arg_count(struct mjs* mjs, size_t count) {
size_t num_args = mjs_nargs(mjs);
if(num_args != count) {
ret_bad_args(mjs, "Wrong argument count");
return false;
}
return true;
}

static bool get_int_arg(struct mjs* mjs, size_t index, uint8_t* value, bool error) {
mjs_val_t int_obj = mjs_arg(mjs, index);
if(!mjs_is_number(int_obj)) {
if(error) ret_bad_args(mjs, "Argument must be a number");
return false;
}
*value = mjs_get_int(mjs, int_obj);
return true;
}

static void js_blebeacon_set_config(struct mjs* mjs) {
JsBlebeaconInst* blebeacon = get_this_ctx(mjs);
if(mjs_nargs(mjs) < 1 || mjs_nargs(mjs) > 4) {
ret_bad_args(mjs, "Wrong argument count");
return;
}

char* mac = NULL;
size_t mac_len = 0;
mjs_val_t mac_arg = mjs_arg(mjs, 0);
if(mjs_is_typed_array(mac_arg)) {
if(mjs_is_data_view(mac_arg)) {
mac_arg = mjs_dataview_get_buf(mjs, mac_arg);
}
mac = mjs_array_buf_get_ptr(mjs, mac_arg, &mac_len);
}
if(!mac || mac_len != EXTRA_BEACON_MAC_ADDR_SIZE) {
ret_bad_args(mjs, "Wrong MAC address");
return;
}

uint8_t power = GapAdvPowerLevel_0dBm;
get_int_arg(mjs, 1, &power, false);
power = CLAMP(power, GapAdvPowerLevel_6dBm, GapAdvPowerLevel_Neg40dBm);

uint8_t intv_min = 50;
get_int_arg(mjs, 2, &intv_min, false);
intv_min = MAX(intv_min, 20);

uint8_t intv_max = 150;
get_int_arg(mjs, 3, &intv_max, false);
intv_max = MAX(intv_max, intv_min);

GapExtraBeaconConfig config = {
.min_adv_interval_ms = intv_min,
.max_adv_interval_ms = intv_max,
.adv_channel_map = GapAdvChannelMapAll,
.adv_power_level = power,
.address_type = GapAddressTypePublic,
};
memcpy(config.address, (uint8_t*)mac, sizeof(config.address));

if(!blebeacon->saved_prev_cfg) {
blebeacon->saved_prev_cfg = true;
const GapExtraBeaconConfig* prev_cfg_ptr = furi_hal_bt_extra_beacon_get_config();
if(prev_cfg_ptr) {
blebeacon->prev_cfg_set = true;
memcpy(&blebeacon->prev_cfg, prev_cfg_ptr, sizeof(blebeacon->prev_cfg));
} else {
blebeacon->prev_cfg_set = false;
}
}
furi_check(furi_hal_bt_extra_beacon_set_config(&config));
}

static void js_blebeacon_set_data(struct mjs* mjs) {
JsBlebeaconInst* blebeacon = get_this_ctx(mjs);
if(!check_arg_count(mjs, 1)) return;

char* data = NULL;
size_t data_len = 0;
mjs_val_t data_arg = mjs_arg(mjs, 0);
if(mjs_is_typed_array(data_arg)) {
if(mjs_is_data_view(data_arg)) {
data_arg = mjs_dataview_get_buf(mjs, data_arg);
}
data = mjs_array_buf_get_ptr(mjs, data_arg, &data_len);
}
if(!data) {
ret_bad_args(mjs, "Data must be a Uint8Array");
return;
}

if(!blebeacon->saved_prev_data) {
blebeacon->saved_prev_data = true;
blebeacon->prev_data_len = furi_hal_bt_extra_beacon_get_data(blebeacon->prev_data);
}
furi_check(furi_hal_bt_extra_beacon_set_data((uint8_t*)data, data_len));

mjs_return(mjs, MJS_UNDEFINED);
}

static void js_blebeacon_start(struct mjs* mjs) {
JsBlebeaconInst* blebeacon = get_this_ctx(mjs);
if(!check_arg_count(mjs, 0)) return;

if(!blebeacon->saved_prev_active) {
blebeacon->saved_prev_active = true;
blebeacon->prev_active = furi_hal_bt_extra_beacon_is_active();
}
furi_check(furi_hal_bt_extra_beacon_start());

mjs_return(mjs, MJS_UNDEFINED);
}

static void js_blebeacon_stop(struct mjs* mjs) {
JsBlebeaconInst* blebeacon = get_this_ctx(mjs);
if(!check_arg_count(mjs, 0)) return;
UNUSED(blebeacon);

furi_hal_bt_extra_beacon_stop();

mjs_return(mjs, MJS_UNDEFINED);
}

static void js_blebeacon_keep_alive(struct mjs* mjs) {
JsBlebeaconInst* blebeacon = get_this_ctx(mjs);
if(!check_arg_count(mjs, 1)) return;

mjs_val_t bool_obj = mjs_arg(mjs, 0);
blebeacon->keep_alive = mjs_get_bool(mjs, bool_obj);

mjs_return(mjs, MJS_UNDEFINED);
}

static void* js_blebeacon_create(struct mjs* mjs, mjs_val_t* object) {
JsBlebeaconInst* blebeacon = malloc(sizeof(JsBlebeaconInst));
mjs_val_t blebeacon_obj = mjs_mk_object(mjs);
mjs_set(mjs, blebeacon_obj, INST_PROP_NAME, ~0, mjs_mk_foreign(mjs, blebeacon));
mjs_set(mjs, blebeacon_obj, "setConfig", ~0, MJS_MK_FN(js_blebeacon_set_config));
mjs_set(mjs, blebeacon_obj, "setData", ~0, MJS_MK_FN(js_blebeacon_set_data));
mjs_set(mjs, blebeacon_obj, "start", ~0, MJS_MK_FN(js_blebeacon_start));
mjs_set(mjs, blebeacon_obj, "stop", ~0, MJS_MK_FN(js_blebeacon_stop));
mjs_set(mjs, blebeacon_obj, "keepAlive", ~0, MJS_MK_FN(js_blebeacon_keep_alive));
*object = blebeacon_obj;
return blebeacon;
}

static void js_blebeacon_destroy(void* inst) {
JsBlebeaconInst* blebeacon = inst;
if(!blebeacon->keep_alive) {
if(furi_hal_bt_extra_beacon_is_active()) {
furi_hal_bt_extra_beacon_stop();
}
if(blebeacon->saved_prev_cfg && blebeacon->prev_cfg_set) {
furi_check(furi_hal_bt_extra_beacon_set_config(&blebeacon->prev_cfg));
}
if(blebeacon->saved_prev_data) {
furi_check(
furi_hal_bt_extra_beacon_set_data(blebeacon->prev_data, blebeacon->prev_data_len));
}
if(blebeacon->prev_active) {
furi_check(furi_hal_bt_extra_beacon_start());
}
}
free(blebeacon);
}

static const JsModuleDescriptor js_blebeacon_desc = {
"blebeacon",
js_blebeacon_create,
js_blebeacon_destroy,
};

static const FlipperAppPluginDescriptor plugin_descriptor = {
.appid = PLUGIN_APP_ID,
.ep_api_version = PLUGIN_API_VERSION,
.entry_point = &js_blebeacon_desc,
};

const FlipperAppPluginDescriptor* js_blebeacon_ep(void) {
return &plugin_descriptor;
}
Loading

0 comments on commit 0c36944

Please sign in to comment.