mirror of
https://github.com/DarkFlippers/unleashed-firmware
synced 2024-11-26 06:20:21 +00:00
Various changes, in expansion, rgb, js
by Willy-JL , Spooks4576 , Sil333033 , HaxSam
This commit is contained in:
parent
1bd56fb190
commit
c9434492b8
19 changed files with 1665 additions and 12 deletions
|
@ -527,7 +527,7 @@ index 0000000..572e1df
|
|||
+
|
||||
+void SK6805_update(void) {
|
||||
+ SK6805_init();
|
||||
+ furi_kernel_lock();
|
||||
+ FURI_CRITICAL_ENTER();
|
||||
+ uint32_t end;
|
||||
+ /* Последовательная отправка цветов светодиодов */
|
||||
+ for(uint8_t lednumber = 0; lednumber < SK6805_LED_COUNT; lednumber++) {
|
||||
|
@ -567,7 +567,7 @@ index 0000000..572e1df
|
|||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ furi_kernel_unlock();
|
||||
+ FURI_CRITICAL_EXIT();
|
||||
+}
|
||||
diff --git a/lib/drivers/SK6805.h b/lib/drivers/SK6805.h
|
||||
new file mode 100644
|
||||
|
|
|
@ -18,6 +18,7 @@ typedef enum {
|
|||
ExpansionStateDisabled,
|
||||
ExpansionStateEnabled,
|
||||
ExpansionStateRunning,
|
||||
ExpansionStateConnectionEstablished,
|
||||
} ExpansionState;
|
||||
|
||||
typedef enum {
|
||||
|
@ -26,10 +27,15 @@ typedef enum {
|
|||
ExpansionMessageTypeSetListenSerial,
|
||||
ExpansionMessageTypeModuleConnected,
|
||||
ExpansionMessageTypeModuleDisconnected,
|
||||
ExpansionMessageTypeConnectionEstablished,
|
||||
ExpansionMessageTypeIsConnected,
|
||||
} ExpansionMessageType;
|
||||
|
||||
typedef union {
|
||||
FuriHalSerialId serial_id;
|
||||
union {
|
||||
FuriHalSerialId serial_id;
|
||||
bool* is_connected;
|
||||
};
|
||||
} ExpansionMessageData;
|
||||
|
||||
typedef struct {
|
||||
|
@ -68,13 +74,21 @@ static void expansion_detect_callback(void* context) {
|
|||
UNUSED(status);
|
||||
}
|
||||
|
||||
static void expansion_worker_callback(void* context) {
|
||||
static void expansion_worker_callback(void* context, ExpansionWorkerCallbackReason reason) {
|
||||
furi_assert(context);
|
||||
Expansion* instance = context;
|
||||
|
||||
ExpansionMessage message = {
|
||||
.type = ExpansionMessageTypeModuleDisconnected,
|
||||
.api_lock = NULL, // Not locking the API here to avoid a deadlock
|
||||
ExpansionMessage message;
|
||||
switch(reason) {
|
||||
case ExpansionWorkerCallbackReasonExit:
|
||||
message.type = ExpansionMessageTypeModuleDisconnected;
|
||||
message.api_lock = NULL; // Not locking the API here to avoid a deadlock
|
||||
break;
|
||||
|
||||
case ExpansionWorkerCallbackReasonConnected:
|
||||
message.type = ExpansionMessageTypeConnectionEstablished;
|
||||
message.api_lock = api_lock_alloc_locked();
|
||||
break;
|
||||
};
|
||||
|
||||
const FuriStatus status = furi_message_queue_put(instance->queue, &message, FuriWaitForever);
|
||||
|
@ -105,7 +119,9 @@ static void
|
|||
|
||||
if(instance->state == ExpansionStateDisabled) {
|
||||
return;
|
||||
} else if(instance->state == ExpansionStateRunning) {
|
||||
} else if(
|
||||
instance->state == ExpansionStateRunning ||
|
||||
instance->state == ExpansionStateConnectionEstablished) {
|
||||
expansion_worker_stop(instance->worker);
|
||||
expansion_worker_free(instance->worker);
|
||||
} else {
|
||||
|
@ -122,7 +138,8 @@ static void expansion_control_handler_set_listen_serial(
|
|||
const ExpansionMessageData* data) {
|
||||
furi_check(data->serial_id < FuriHalSerialIdMax);
|
||||
|
||||
if(instance->state == ExpansionStateRunning) {
|
||||
if(instance->state == ExpansionStateRunning ||
|
||||
instance->state == ExpansionStateConnectionEstablished) {
|
||||
expansion_worker_stop(instance->worker);
|
||||
expansion_worker_free(instance->worker);
|
||||
|
||||
|
@ -160,7 +177,8 @@ static void expansion_control_handler_module_disconnected(
|
|||
Expansion* instance,
|
||||
const ExpansionMessageData* data) {
|
||||
UNUSED(data);
|
||||
if(instance->state != ExpansionStateRunning) {
|
||||
if(instance->state != ExpansionStateRunning &&
|
||||
instance->state != ExpansionStateConnectionEstablished) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -170,6 +188,23 @@ static void expansion_control_handler_module_disconnected(
|
|||
instance->serial_id, expansion_detect_callback, instance);
|
||||
}
|
||||
|
||||
static void expansion_control_handler_connection_established(
|
||||
Expansion* instance,
|
||||
const ExpansionMessageData* data) {
|
||||
UNUSED(data);
|
||||
if(instance->state != ExpansionStateRunning &&
|
||||
instance->state != ExpansionStateConnectionEstablished) {
|
||||
return;
|
||||
}
|
||||
|
||||
instance->state = ExpansionStateConnectionEstablished;
|
||||
}
|
||||
|
||||
static void
|
||||
expansion_control_handler_is_connected(Expansion* instance, const ExpansionMessageData* data) {
|
||||
*data->is_connected = instance->state == ExpansionStateConnectionEstablished;
|
||||
}
|
||||
|
||||
typedef void (*ExpansionControlHandler)(Expansion*, const ExpansionMessageData*);
|
||||
|
||||
static const ExpansionControlHandler expansion_control_handlers[] = {
|
||||
|
@ -178,6 +213,8 @@ static const ExpansionControlHandler expansion_control_handlers[] = {
|
|||
[ExpansionMessageTypeSetListenSerial] = expansion_control_handler_set_listen_serial,
|
||||
[ExpansionMessageTypeModuleConnected] = expansion_control_handler_module_connected,
|
||||
[ExpansionMessageTypeModuleDisconnected] = expansion_control_handler_module_disconnected,
|
||||
[ExpansionMessageTypeConnectionEstablished] = expansion_control_handler_connection_established,
|
||||
[ExpansionMessageTypeIsConnected] = expansion_control_handler_is_connected,
|
||||
};
|
||||
|
||||
static int32_t expansion_control(void* context) {
|
||||
|
@ -249,6 +286,22 @@ void expansion_disable(Expansion* instance) {
|
|||
api_lock_wait_unlock_and_free(message.api_lock);
|
||||
}
|
||||
|
||||
bool expansion_is_connected(Expansion* instance) {
|
||||
furi_check(instance);
|
||||
bool is_connected;
|
||||
|
||||
ExpansionMessage message = {
|
||||
.type = ExpansionMessageTypeIsConnected,
|
||||
.data.is_connected = &is_connected,
|
||||
.api_lock = api_lock_alloc_locked(),
|
||||
};
|
||||
|
||||
furi_message_queue_put(instance->queue, &message, FuriWaitForever);
|
||||
api_lock_wait_unlock_and_free(message.api_lock);
|
||||
|
||||
return is_connected;
|
||||
}
|
||||
|
||||
void expansion_set_listen_serial(Expansion* instance, FuriHalSerialId serial_id) {
|
||||
furi_check(instance);
|
||||
furi_check(serial_id < FuriHalSerialIdMax);
|
||||
|
|
|
@ -50,6 +50,15 @@ void expansion_enable(Expansion* instance);
|
|||
*/
|
||||
void expansion_disable(Expansion* instance);
|
||||
|
||||
/**
|
||||
* @brief Check if an expansion module is connected.
|
||||
*
|
||||
* @param[in,out] instance pointer to the Expansion instance.
|
||||
*
|
||||
* @returns true if the module is connected and initialized, false otherwise.
|
||||
*/
|
||||
bool expansion_is_connected(Expansion* instance);
|
||||
|
||||
/**
|
||||
* @brief Enable support for expansion modules on designated serial port.
|
||||
*
|
||||
|
|
|
@ -223,6 +223,7 @@ static bool expansion_worker_handle_state_handshake(
|
|||
|
||||
if(furi_hal_serial_is_baud_rate_supported(instance->serial_handle, baud_rate)) {
|
||||
instance->state = ExpansionWorkerStateConnected;
|
||||
instance->callback(instance->cb_context, ExpansionWorkerCallbackReasonConnected);
|
||||
// Send response at previous baud rate
|
||||
if(!expansion_worker_send_status_response(instance, ExpansionFrameErrorNone)) break;
|
||||
furi_hal_serial_set_br(instance->serial_handle, baud_rate);
|
||||
|
@ -351,7 +352,7 @@ static int32_t expansion_worker(void* context) {
|
|||
|
||||
// Do not invoke worker callback on user-requested exit
|
||||
if((instance->exit_reason != ExpansionWorkerExitReasonUser) && (instance->callback != NULL)) {
|
||||
instance->callback(instance->cb_context);
|
||||
instance->callback(instance->cb_context, ExpansionWorkerCallbackReasonExit);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -17,14 +17,20 @@
|
|||
*/
|
||||
typedef struct ExpansionWorker ExpansionWorker;
|
||||
|
||||
typedef enum {
|
||||
ExpansionWorkerCallbackReasonExit,
|
||||
ExpansionWorkerCallbackReasonConnected,
|
||||
} ExpansionWorkerCallbackReason;
|
||||
|
||||
/**
|
||||
* @brief Worker callback type.
|
||||
*
|
||||
* @see expansion_worker_set_callback()
|
||||
*
|
||||
* @param[in,out] context pointer to a user-defined object.
|
||||
* @param[in] reason reason for the callback.
|
||||
*/
|
||||
typedef void (*ExpansionWorkerCallback)(void* context);
|
||||
typedef void (*ExpansionWorkerCallback)(void* context, ExpansionWorkerCallbackReason reason);
|
||||
|
||||
/**
|
||||
* @brief Create an expansion worker instance.
|
||||
|
|
|
@ -47,3 +47,42 @@ App(
|
|||
requires=["js_app"],
|
||||
sources=["modules/js_usbdisk/*.c"],
|
||||
)
|
||||
|
||||
App(
|
||||
appid="js_submenu",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="js_submenu_ep",
|
||||
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"],
|
||||
)
|
||||
App(
|
||||
appid="js_subghz",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="js_subghz_ep",
|
||||
requires=["js_app"],
|
||||
sources=["modules/js_subghz/*.c"],
|
||||
)
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
let blebeacon = require("blebeacon");
|
||||
|
||||
// Stop if previous background beacon is active
|
||||
if (blebeacon.isActive()) {
|
||||
blebeacon.stop();
|
||||
}
|
||||
|
||||
// Make sure it resets at script exit, true will keep advertising in background
|
||||
// This is false by default, can be omitted
|
||||
blebeacon.keepAlive(false);
|
||||
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
while (true) {
|
||||
sendRandomModelAdvertisement();
|
||||
}
|
19
applications/system/js_app/examples/apps/Scripts/keyboard.js
Normal file
19
applications/system/js_app/examples/apps/Scripts/keyboard.js
Normal file
|
@ -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
applications/system/js_app/examples/apps/Scripts/math.js
Normal file
47
applications/system/js_app/examples/apps/Scripts/math.js
Normal file
|
@ -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);
|
37
applications/system/js_app/examples/apps/Scripts/subghz.js
Normal file
37
applications/system/js_app/examples/apps/Scripts/subghz.js
Normal file
|
@ -0,0 +1,37 @@
|
|||
let subghz = require("subghz");
|
||||
subghz.setup();
|
||||
|
||||
function printRXline() {
|
||||
if (subghz.getState() !== "RX") {
|
||||
subghz.setRx(); // to RX
|
||||
}
|
||||
|
||||
let rssi = subghz.getRssi();
|
||||
let freq = subghz.getFrequency();
|
||||
let ext = subghz.isExternal();
|
||||
|
||||
print("rssi: ", rssi, "dBm", "@", freq, "MHz", "ext: ", ext);
|
||||
}
|
||||
|
||||
function changeFrequency(freq) {
|
||||
if (subghz.getState() !== "IDLE") {
|
||||
subghz.setIdle(); // need to be idle to change frequency
|
||||
}
|
||||
subghz.setFrequency(freq);
|
||||
}
|
||||
|
||||
subghz.setIdle();
|
||||
print(subghz.getState()); // "IDLE"
|
||||
subghz.setRx();
|
||||
print(subghz.getState()); // "RX"
|
||||
|
||||
changeFrequency(433920000);
|
||||
printRXline();
|
||||
delay(1000);
|
||||
|
||||
let result = subghz.transmitFile("/ext/subghz/0.sub");
|
||||
print(result ? "Send success" : "Send failed");
|
||||
delay(1000);
|
||||
|
||||
changeFrequency(315000000);
|
||||
printRXline();
|
11
applications/system/js_app/examples/apps/Scripts/submenu.js
Normal file
11
applications/system/js_app/examples/apps/Scripts/submenu.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
let submenu = require("submenu");
|
||||
|
||||
submenu.addItem("Item 1", 0);
|
||||
submenu.addItem("Item 2", 1);
|
||||
submenu.addItem("Item 3", 2);
|
||||
|
||||
submenu.setHeader("Select an option:");
|
||||
|
||||
let result = submenu.show();
|
||||
|
||||
print("Result:", result);
|
245
applications/system/js_app/modules/js_blebeacon.c
Normal file
245
applications/system/js_app/modules/js_blebeacon.c
Normal file
|
@ -0,0 +1,245 @@
|
|||
#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 void ret_int_err(struct mjs* mjs, const char* error) {
|
||||
mjs_prepend_errorf(mjs, MJS_INTERNAL_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_is_active(struct mjs* mjs) {
|
||||
JsBlebeaconInst* blebeacon = get_this_ctx(mjs);
|
||||
if(!check_arg_count(mjs, 0)) return;
|
||||
UNUSED(blebeacon);
|
||||
|
||||
mjs_return(mjs, mjs_mk_boolean(mjs, furi_hal_bt_extra_beacon_is_active()));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
if(!furi_hal_bt_extra_beacon_set_config(&config)) {
|
||||
ret_int_err(mjs, "Failed setting beacon config");
|
||||
return;
|
||||
}
|
||||
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
if(!furi_hal_bt_extra_beacon_set_data((uint8_t*)data, data_len)) {
|
||||
ret_int_err(mjs, "Failed setting beacon data");
|
||||
return;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
if(!furi_hal_bt_extra_beacon_start()) {
|
||||
ret_int_err(mjs, "Failed starting beacon");
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if(!blebeacon->saved_prev_active) {
|
||||
blebeacon->saved_prev_active = true;
|
||||
blebeacon->prev_active = furi_hal_bt_extra_beacon_is_active();
|
||||
}
|
||||
if(!furi_hal_bt_extra_beacon_stop()) {
|
||||
ret_int_err(mjs, "Failed stopping beacon");
|
||||
return;
|
||||
}
|
||||
|
||||
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, "isActive", ~0, MJS_MK_FN(js_blebeacon_is_active));
|
||||
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_check(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;
|
||||
}
|
195
applications/system/js_app/modules/js_keyboard.c
Normal file
195
applications/system/js_app/modules/js_keyboard.c
Normal file
|
@ -0,0 +1,195 @@
|
|||
#include "../js_modules.h"
|
||||
#include <gui/modules/text_input.h>
|
||||
#include <gui/modules/byte_input.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
|
||||
#define membersof(x) (sizeof(x) / sizeof(x[0]))
|
||||
|
||||
typedef struct {
|
||||
char* data;
|
||||
TextInput* text_input;
|
||||
ByteInput* byte_input;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
uint8_t* byteinput;
|
||||
} JsKeyboardInst;
|
||||
|
||||
typedef enum {
|
||||
JsKeyboardViewTextInput,
|
||||
JsKeyboardViewByteInput,
|
||||
} JsKeyboardView;
|
||||
|
||||
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 get_str_arg(struct mjs* mjs, size_t index, const char** value, bool error) {
|
||||
mjs_val_t str_obj = mjs_arg(mjs, index);
|
||||
if(!mjs_is_string(str_obj)) {
|
||||
if(error) ret_bad_args(mjs, "Argument must be a string");
|
||||
return false;
|
||||
}
|
||||
size_t str_len = 0;
|
||||
*value = mjs_get_string(mjs, &str_obj, &str_len);
|
||||
if((str_len == 0) || (*value == NULL)) {
|
||||
if(error) ret_bad_args(mjs, "Bad string argument");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool get_int_arg(struct mjs* mjs, size_t index, size_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 JsKeyboardInst* get_this_ctx(struct mjs* mjs) {
|
||||
mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);
|
||||
JsKeyboardInst* storage = mjs_get_ptr(mjs, obj_inst);
|
||||
furi_assert(storage);
|
||||
return storage;
|
||||
}
|
||||
|
||||
void text_input_callback(void* context) {
|
||||
JsKeyboardInst* keyboard = (JsKeyboardInst*)context;
|
||||
view_dispatcher_stop(keyboard->view_dispatcher);
|
||||
}
|
||||
|
||||
void byte_input_callback(void* context) {
|
||||
JsKeyboardInst* keyboard = (JsKeyboardInst*)context;
|
||||
view_dispatcher_stop(keyboard->view_dispatcher);
|
||||
}
|
||||
|
||||
static void js_keyboard_set_header(struct mjs* mjs) {
|
||||
JsKeyboardInst* keyboard = get_this_ctx(mjs);
|
||||
|
||||
const char* header;
|
||||
if(!get_str_arg(mjs, 0, &header, true)) return;
|
||||
|
||||
text_input_set_header_text(keyboard->text_input, header);
|
||||
byte_input_set_header_text(keyboard->byte_input, header);
|
||||
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
}
|
||||
|
||||
static void js_keyboard_text(struct mjs* mjs) {
|
||||
JsKeyboardInst* keyboard = get_this_ctx(mjs);
|
||||
|
||||
size_t input_length;
|
||||
if(!get_int_arg(mjs, 0, &input_length, true)) return;
|
||||
char* buffer = malloc(input_length);
|
||||
|
||||
const char* default_text = "";
|
||||
bool clear_default = false;
|
||||
if(get_str_arg(mjs, 1, &default_text, false)) {
|
||||
strlcpy(buffer, default_text, input_length);
|
||||
mjs_val_t bool_obj = mjs_arg(mjs, 2);
|
||||
clear_default = mjs_get_bool(mjs, bool_obj);
|
||||
}
|
||||
|
||||
view_dispatcher_attach_to_gui(
|
||||
keyboard->view_dispatcher, furi_record_open(RECORD_GUI), ViewDispatcherTypeFullscreen);
|
||||
furi_record_close(RECORD_GUI);
|
||||
|
||||
text_input_set_result_callback(
|
||||
keyboard->text_input, text_input_callback, keyboard, buffer, input_length, clear_default);
|
||||
|
||||
view_dispatcher_switch_to_view(keyboard->view_dispatcher, JsKeyboardViewTextInput);
|
||||
|
||||
view_dispatcher_run(keyboard->view_dispatcher);
|
||||
|
||||
text_input_reset(keyboard->text_input);
|
||||
|
||||
mjs_return(mjs, mjs_mk_string(mjs, buffer, ~0, true));
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
static void js_keyboard_byte(struct mjs* mjs) {
|
||||
JsKeyboardInst* keyboard = get_this_ctx(mjs);
|
||||
|
||||
size_t input_length;
|
||||
if(!get_int_arg(mjs, 0, &input_length, true)) return;
|
||||
uint8_t* buffer = malloc(input_length);
|
||||
|
||||
mjs_val_t default_data_arg = mjs_arg(mjs, 1);
|
||||
if(mjs_is_typed_array(default_data_arg)) {
|
||||
if(mjs_is_data_view(default_data_arg)) {
|
||||
default_data_arg = mjs_dataview_get_buf(mjs, default_data_arg);
|
||||
}
|
||||
size_t default_data_len = 0;
|
||||
char* default_data = mjs_array_buf_get_ptr(mjs, default_data_arg, &default_data_len);
|
||||
memcpy(buffer, (uint8_t*)default_data, MIN((size_t)input_length, default_data_len));
|
||||
}
|
||||
|
||||
view_dispatcher_attach_to_gui(
|
||||
keyboard->view_dispatcher, furi_record_open(RECORD_GUI), ViewDispatcherTypeFullscreen);
|
||||
furi_record_close(RECORD_GUI);
|
||||
|
||||
byte_input_set_result_callback(
|
||||
keyboard->byte_input, byte_input_callback, NULL, keyboard, buffer, input_length);
|
||||
|
||||
view_dispatcher_switch_to_view(keyboard->view_dispatcher, JsKeyboardViewByteInput);
|
||||
|
||||
view_dispatcher_run(keyboard->view_dispatcher);
|
||||
|
||||
byte_input_set_result_callback(keyboard->byte_input, NULL, NULL, NULL, NULL, 0);
|
||||
byte_input_set_header_text(keyboard->byte_input, "");
|
||||
|
||||
mjs_return(mjs, mjs_mk_array_buf(mjs, (char*)buffer, input_length));
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
static void* js_keyboard_create(struct mjs* mjs, mjs_val_t* object) {
|
||||
JsKeyboardInst* keyboard = malloc(sizeof(JsKeyboardInst));
|
||||
mjs_val_t keyboard_obj = mjs_mk_object(mjs);
|
||||
mjs_set(mjs, keyboard_obj, INST_PROP_NAME, ~0, mjs_mk_foreign(mjs, keyboard));
|
||||
mjs_set(mjs, keyboard_obj, "setHeader", ~0, MJS_MK_FN(js_keyboard_set_header));
|
||||
mjs_set(mjs, keyboard_obj, "text", ~0, MJS_MK_FN(js_keyboard_text));
|
||||
mjs_set(mjs, keyboard_obj, "byte", ~0, MJS_MK_FN(js_keyboard_byte));
|
||||
keyboard->byte_input = byte_input_alloc();
|
||||
keyboard->text_input = text_input_alloc();
|
||||
keyboard->view_dispatcher = view_dispatcher_alloc();
|
||||
view_dispatcher_enable_queue(keyboard->view_dispatcher);
|
||||
view_dispatcher_add_view(
|
||||
keyboard->view_dispatcher,
|
||||
JsKeyboardViewTextInput,
|
||||
text_input_get_view(keyboard->text_input));
|
||||
view_dispatcher_add_view(
|
||||
keyboard->view_dispatcher,
|
||||
JsKeyboardViewByteInput,
|
||||
byte_input_get_view(keyboard->byte_input));
|
||||
*object = keyboard_obj;
|
||||
return keyboard;
|
||||
}
|
||||
|
||||
static void js_keyboard_destroy(void* inst) {
|
||||
JsKeyboardInst* keyboard = inst;
|
||||
view_dispatcher_remove_view(keyboard->view_dispatcher, JsKeyboardViewByteInput);
|
||||
byte_input_free(keyboard->byte_input);
|
||||
view_dispatcher_remove_view(keyboard->view_dispatcher, JsKeyboardViewTextInput);
|
||||
text_input_free(keyboard->text_input);
|
||||
view_dispatcher_free(keyboard->view_dispatcher);
|
||||
free(keyboard->data);
|
||||
free(keyboard);
|
||||
}
|
||||
|
||||
static const JsModuleDescriptor js_keyboard_desc = {
|
||||
"keyboard",
|
||||
js_keyboard_create,
|
||||
js_keyboard_destroy,
|
||||
};
|
||||
|
||||
static const FlipperAppPluginDescriptor plugin_descriptor = {
|
||||
.appid = PLUGIN_APP_ID,
|
||||
.ep_api_version = PLUGIN_API_VERSION,
|
||||
.entry_point = &js_keyboard_desc,
|
||||
};
|
||||
|
||||
const FlipperAppPluginDescriptor* js_keyboard_ep(void) {
|
||||
return &plugin_descriptor;
|
||||
}
|
309
applications/system/js_app/modules/js_math.c
Normal file
309
applications/system/js_app/modules/js_math.c
Normal file
|
@ -0,0 +1,309 @@
|
|||
#include "../js_modules.h"
|
||||
#include "furi_hal_random.h"
|
||||
|
||||
#define JS_MATH_PI (double)3.14159265358979323846
|
||||
#define JS_MATH_E (double)2.7182818284590452354
|
||||
|
||||
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_mk_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;
|
||||
}
|
||||
|
||||
void js_math_abs(struct mjs* mjs) {
|
||||
if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) {
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
}
|
||||
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
|
||||
mjs_return(mjs, x < 0 ? mjs_mk_number(mjs, -x) : mjs_arg(mjs, 0));
|
||||
}
|
||||
|
||||
void js_math_acos(struct mjs* mjs) {
|
||||
if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) {
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
}
|
||||
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
|
||||
if(x < -1 || x > 1) {
|
||||
ret_bad_args(mjs, "Invalid input value for Math.acos");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
}
|
||||
mjs_return(mjs, mjs_mk_number(mjs, JS_MATH_PI / (double)2 - atan(x / sqrt(1 - x * x))));
|
||||
}
|
||||
|
||||
void js_math_acosh(struct mjs* mjs) {
|
||||
if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) {
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
}
|
||||
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
|
||||
if(x < 1) {
|
||||
ret_bad_args(mjs, "Invalid input value for Math.acosh");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
}
|
||||
mjs_return(mjs, mjs_mk_number(mjs, log(x + sqrt(x * x - 1))));
|
||||
}
|
||||
|
||||
void js_math_asin(struct mjs* mjs) {
|
||||
if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) {
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
}
|
||||
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
|
||||
mjs_return(mjs, mjs_mk_number(mjs, atan(x / sqrt(1 - x * x))));
|
||||
}
|
||||
|
||||
void js_math_asinh(struct mjs* mjs) {
|
||||
if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) {
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
}
|
||||
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
|
||||
mjs_return(mjs, mjs_mk_number(mjs, log(x + sqrt(x * x + 1))));
|
||||
}
|
||||
|
||||
void js_math_atan(struct mjs* mjs) {
|
||||
if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) {
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
}
|
||||
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
|
||||
mjs_return(mjs, mjs_mk_number(mjs, atan(x)));
|
||||
}
|
||||
|
||||
void js_math_atan2(struct mjs* mjs) {
|
||||
if(!check_arg_count(mjs, 2) || !mjs_is_number(mjs_arg(mjs, 0)) ||
|
||||
!mjs_is_number(mjs_arg(mjs, 1))) {
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
}
|
||||
double y = mjs_get_double(mjs, mjs_arg(mjs, 0));
|
||||
double x = mjs_get_double(mjs, mjs_arg(mjs, 1));
|
||||
mjs_return(mjs, mjs_mk_number(mjs, atan2(y, x)));
|
||||
}
|
||||
|
||||
void js_math_atanh(struct mjs* mjs) {
|
||||
if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) {
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
}
|
||||
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
|
||||
if(x <= -1 || x >= 1) {
|
||||
ret_bad_args(mjs, "Invalid input value for Math.atanh");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
}
|
||||
mjs_return(mjs, mjs_mk_number(mjs, (double)0.5 * log((1 + x) / (1 - x))));
|
||||
}
|
||||
|
||||
void js_math_cbrt(struct mjs* mjs) {
|
||||
if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) {
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
}
|
||||
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
|
||||
mjs_return(mjs, mjs_mk_number(mjs, pow(x, 1.0 / 3.0)));
|
||||
}
|
||||
|
||||
void js_math_ceil(struct mjs* mjs) {
|
||||
if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) {
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
}
|
||||
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
|
||||
mjs_return(mjs, mjs_mk_number(mjs, (int)(x + (double)0.5)));
|
||||
}
|
||||
|
||||
void js_math_clz32(struct mjs* mjs) {
|
||||
if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) {
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
}
|
||||
unsigned int x = (unsigned int)mjs_get_int(mjs, mjs_arg(mjs, 0));
|
||||
int count = 0;
|
||||
while(x) {
|
||||
x >>= 1;
|
||||
count++;
|
||||
}
|
||||
mjs_return(mjs, mjs_mk_number(mjs, 32 - count));
|
||||
}
|
||||
|
||||
void js_math_cos(struct mjs* mjs) {
|
||||
if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) {
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
}
|
||||
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
|
||||
mjs_return(mjs, mjs_mk_number(mjs, cos(x)));
|
||||
}
|
||||
|
||||
void js_math_exp(struct mjs* mjs) {
|
||||
if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) {
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
}
|
||||
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
|
||||
double result = 1;
|
||||
double term = 1;
|
||||
for(int i = 1; i < 100; i++) {
|
||||
term *= x / i;
|
||||
result += term;
|
||||
}
|
||||
mjs_return(mjs, mjs_mk_number(mjs, result));
|
||||
}
|
||||
|
||||
void js_math_floor(struct mjs* mjs) {
|
||||
if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) {
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
}
|
||||
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
|
||||
mjs_return(mjs, mjs_mk_number(mjs, (int)x));
|
||||
}
|
||||
|
||||
void js_math_log(struct mjs* mjs) {
|
||||
if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) {
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
}
|
||||
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
|
||||
if(x <= 0) {
|
||||
ret_bad_args(mjs, "Invalid input value for Math.log");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
}
|
||||
double result = 0;
|
||||
while(x >= JS_MATH_E) {
|
||||
x /= JS_MATH_E;
|
||||
result++;
|
||||
}
|
||||
mjs_return(mjs, mjs_mk_number(mjs, result + log(x)));
|
||||
}
|
||||
|
||||
void js_math_max(struct mjs* mjs) {
|
||||
if(!check_arg_count(mjs, 2) || !mjs_is_number(mjs_arg(mjs, 0)) ||
|
||||
!mjs_is_number(mjs_arg(mjs, 1))) {
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
}
|
||||
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
|
||||
double y = mjs_get_double(mjs, mjs_arg(mjs, 1));
|
||||
mjs_return(mjs, mjs_mk_number(mjs, x > y ? x : y));
|
||||
}
|
||||
|
||||
void js_math_min(struct mjs* mjs) {
|
||||
if(!check_arg_count(mjs, 2) || !mjs_is_number(mjs_arg(mjs, 0)) ||
|
||||
!mjs_is_number(mjs_arg(mjs, 1))) {
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
}
|
||||
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
|
||||
double y = mjs_get_double(mjs, mjs_arg(mjs, 1));
|
||||
mjs_return(mjs, mjs_mk_number(mjs, x < y ? x : y));
|
||||
}
|
||||
|
||||
void js_math_pow(struct mjs* mjs) {
|
||||
if(!check_arg_count(mjs, 2) || !mjs_is_number(mjs_arg(mjs, 0)) ||
|
||||
!mjs_is_number(mjs_arg(mjs, 1))) {
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
}
|
||||
double base = mjs_get_double(mjs, mjs_arg(mjs, 0));
|
||||
double exponent = mjs_get_double(mjs, mjs_arg(mjs, 1));
|
||||
double result = 1;
|
||||
for(int i = 0; i < exponent; i++) {
|
||||
result *= base;
|
||||
}
|
||||
mjs_return(mjs, mjs_mk_number(mjs, result));
|
||||
}
|
||||
|
||||
void js_math_random(struct mjs* mjs) {
|
||||
if(!check_arg_count(mjs, 0)) {
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
}
|
||||
const uint32_t random_val = furi_hal_random_get();
|
||||
double rnd = (double)random_val / RAND_MAX;
|
||||
mjs_return(mjs, mjs_mk_number(mjs, rnd));
|
||||
}
|
||||
|
||||
void js_math_sign(struct mjs* mjs) {
|
||||
if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) {
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
}
|
||||
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
|
||||
mjs_return(mjs, mjs_mk_number(mjs, x == 0 ? 0 : (x < 0 ? -1 : 1)));
|
||||
}
|
||||
|
||||
void js_math_sin(struct mjs* mjs) {
|
||||
if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) {
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
}
|
||||
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
|
||||
double result = x;
|
||||
double term = x;
|
||||
for(int i = 1; i < 10; i++) {
|
||||
term *= -x * x / ((2 * i) * (2 * i + 1));
|
||||
result += term;
|
||||
}
|
||||
mjs_return(mjs, mjs_mk_number(mjs, result));
|
||||
}
|
||||
|
||||
void js_math_sqrt(struct mjs* mjs) {
|
||||
if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) {
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
}
|
||||
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
|
||||
if(x < 0) {
|
||||
ret_bad_args(mjs, "Invalid input value for Math.sqrt");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
}
|
||||
double result = 1;
|
||||
while(result * result < x) {
|
||||
result += (double)0.001;
|
||||
}
|
||||
mjs_return(mjs, mjs_mk_number(mjs, result));
|
||||
}
|
||||
|
||||
void js_math_trunc(struct mjs* mjs) {
|
||||
if(!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) {
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
}
|
||||
double x = mjs_get_double(mjs, mjs_arg(mjs, 0));
|
||||
mjs_return(mjs, mjs_mk_number(mjs, x < 0 ? ceil(x) : floor(x)));
|
||||
}
|
||||
|
||||
static void* js_math_create(struct mjs* mjs, mjs_val_t* object) {
|
||||
mjs_val_t math_obj = mjs_mk_object(mjs);
|
||||
mjs_set(mjs, math_obj, "abs", ~0, MJS_MK_FN(js_math_abs));
|
||||
mjs_set(mjs, math_obj, "acos", ~0, MJS_MK_FN(js_math_acos));
|
||||
mjs_set(mjs, math_obj, "acosh", ~0, MJS_MK_FN(js_math_acosh));
|
||||
mjs_set(mjs, math_obj, "asin", ~0, MJS_MK_FN(js_math_asin));
|
||||
mjs_set(mjs, math_obj, "asinh", ~0, MJS_MK_FN(js_math_asinh));
|
||||
mjs_set(mjs, math_obj, "atan", ~0, MJS_MK_FN(js_math_atan));
|
||||
mjs_set(mjs, math_obj, "atan2", ~0, MJS_MK_FN(js_math_atan2));
|
||||
mjs_set(mjs, math_obj, "atanh", ~0, MJS_MK_FN(js_math_atanh));
|
||||
mjs_set(mjs, math_obj, "cbrt", ~0, MJS_MK_FN(js_math_cbrt));
|
||||
mjs_set(mjs, math_obj, "ceil", ~0, MJS_MK_FN(js_math_ceil));
|
||||
mjs_set(mjs, math_obj, "clz32", ~0, MJS_MK_FN(js_math_clz32));
|
||||
mjs_set(mjs, math_obj, "cos", ~0, MJS_MK_FN(js_math_cos));
|
||||
mjs_set(mjs, math_obj, "exp", ~0, MJS_MK_FN(js_math_exp));
|
||||
mjs_set(mjs, math_obj, "floor", ~0, MJS_MK_FN(js_math_floor));
|
||||
mjs_set(mjs, math_obj, "log", ~0, MJS_MK_FN(js_math_log));
|
||||
mjs_set(mjs, math_obj, "max", ~0, MJS_MK_FN(js_math_max));
|
||||
mjs_set(mjs, math_obj, "min", ~0, MJS_MK_FN(js_math_min));
|
||||
mjs_set(mjs, math_obj, "pow", ~0, MJS_MK_FN(js_math_pow));
|
||||
mjs_set(mjs, math_obj, "random", ~0, MJS_MK_FN(js_math_random));
|
||||
mjs_set(mjs, math_obj, "sign", ~0, MJS_MK_FN(js_math_sign));
|
||||
mjs_set(mjs, math_obj, "sin", ~0, MJS_MK_FN(js_math_sin));
|
||||
mjs_set(mjs, math_obj, "sqrt", ~0, MJS_MK_FN(js_math_sqrt));
|
||||
mjs_set(mjs, math_obj, "trunc", ~0, MJS_MK_FN(js_math_trunc));
|
||||
mjs_set(mjs, math_obj, "PI", ~0, mjs_mk_number(mjs, JS_MATH_PI));
|
||||
mjs_set(mjs, math_obj, "E", ~0, mjs_mk_number(mjs, JS_MATH_E));
|
||||
*object = math_obj;
|
||||
return (void*)1;
|
||||
}
|
||||
|
||||
static const JsModuleDescriptor js_math_desc = {
|
||||
"math",
|
||||
js_math_create,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const FlipperAppPluginDescriptor plugin_descriptor = {
|
||||
.appid = PLUGIN_APP_ID,
|
||||
.ep_api_version = PLUGIN_API_VERSION,
|
||||
.entry_point = &js_math_desc,
|
||||
};
|
||||
|
||||
const FlipperAppPluginDescriptor* js_math_ep(void) {
|
||||
return &plugin_descriptor;
|
||||
}
|
391
applications/system/js_app/modules/js_subghz/js_subghz.c
Normal file
391
applications/system/js_app/modules/js_subghz/js_subghz.c
Normal file
|
@ -0,0 +1,391 @@
|
|||
#include "../../js_modules.h"
|
||||
#include "radio_device_loader.h"
|
||||
|
||||
#include <lib/subghz/transmitter.h>
|
||||
#include <lib/subghz/devices/devices.h>
|
||||
#include <lib/subghz/protocols/protocol_items.h>
|
||||
|
||||
#include <flipper_format/flipper_format_i.h>
|
||||
|
||||
#define tag "js_subghz"
|
||||
|
||||
typedef enum {
|
||||
JsSubghzRadioStateRX,
|
||||
JsSubghzRadioStateTX,
|
||||
JsSubghzRadioStateIDLE,
|
||||
} JsSubghzRadioState;
|
||||
|
||||
typedef struct {
|
||||
const SubGhzDevice* radio_device;
|
||||
int frequency;
|
||||
bool is_external;
|
||||
JsSubghzRadioState state;
|
||||
} JsSubghzInst;
|
||||
|
||||
// from subghz cli
|
||||
static FuriHalSubGhzPreset js_subghz_get_preset_name(const char* preset_name) {
|
||||
FuriHalSubGhzPreset preset = FuriHalSubGhzPresetIDLE;
|
||||
if(!strcmp(preset_name, "FuriHalSubGhzPresetOok270Async")) {
|
||||
preset = FuriHalSubGhzPresetOok270Async;
|
||||
} else if(!strcmp(preset_name, "FuriHalSubGhzPresetOok650Async")) {
|
||||
preset = FuriHalSubGhzPresetOok650Async;
|
||||
} else if(!strcmp(preset_name, "FuriHalSubGhzPreset2FSKDev238Async")) {
|
||||
preset = FuriHalSubGhzPreset2FSKDev238Async;
|
||||
} else if(!strcmp(preset_name, "FuriHalSubGhzPreset2FSKDev476Async")) {
|
||||
preset = FuriHalSubGhzPreset2FSKDev476Async;
|
||||
} else if(!strcmp(preset_name, "FuriHalSubGhzPresetCustom")) {
|
||||
preset = FuriHalSubGhzPresetCustom;
|
||||
} else {
|
||||
FURI_LOG_I(tag, "unknown preset");
|
||||
}
|
||||
return preset;
|
||||
}
|
||||
|
||||
static int32_t get_int_arg(struct mjs* mjs, size_t index, int32_t* value) {
|
||||
mjs_val_t int_obj = mjs_arg(mjs, index);
|
||||
if(!mjs_is_number(int_obj)) {
|
||||
mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "Argument must be a number");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return false;
|
||||
}
|
||||
*value = mjs_get_int(mjs, int_obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void js_subghz_set_rx(struct mjs* mjs) {
|
||||
mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);
|
||||
JsSubghzInst* js_subghz = mjs_get_ptr(mjs, obj_inst);
|
||||
furi_assert(js_subghz);
|
||||
|
||||
if(js_subghz->state == JsSubghzRadioStateRX) {
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
|
||||
subghz_devices_set_rx(js_subghz->radio_device);
|
||||
js_subghz->state = JsSubghzRadioStateRX;
|
||||
}
|
||||
|
||||
static void js_subgjz_set_idle(struct mjs* mjs) {
|
||||
mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);
|
||||
JsSubghzInst* js_subghz = mjs_get_ptr(mjs, obj_inst);
|
||||
furi_assert(js_subghz);
|
||||
|
||||
if(js_subghz->state == JsSubghzRadioStateIDLE) {
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
|
||||
subghz_devices_idle(js_subghz->radio_device);
|
||||
js_subghz->state = JsSubghzRadioStateIDLE;
|
||||
}
|
||||
|
||||
static void js_subghz_get_rssi(struct mjs* mjs) {
|
||||
mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);
|
||||
JsSubghzInst* js_subghz = mjs_get_ptr(mjs, obj_inst);
|
||||
furi_assert(js_subghz);
|
||||
|
||||
if(js_subghz->state != JsSubghzRadioStateRX) {
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
|
||||
float rssi = subghz_devices_get_rssi(js_subghz->radio_device);
|
||||
mjs_return(mjs, mjs_mk_number(mjs, (double)rssi));
|
||||
}
|
||||
|
||||
static void js_subghz_get_state(struct mjs* mjs) {
|
||||
mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);
|
||||
JsSubghzInst* js_subghz = mjs_get_ptr(mjs, obj_inst);
|
||||
furi_assert(js_subghz);
|
||||
|
||||
const char* state;
|
||||
switch(js_subghz->state) {
|
||||
case JsSubghzRadioStateRX:
|
||||
state = "RX";
|
||||
break;
|
||||
case JsSubghzRadioStateTX:
|
||||
state = "TX";
|
||||
break;
|
||||
case JsSubghzRadioStateIDLE:
|
||||
state = "IDLE";
|
||||
break;
|
||||
default:
|
||||
state = "";
|
||||
break;
|
||||
}
|
||||
|
||||
mjs_return(mjs, mjs_mk_string(mjs, state, ~0, true));
|
||||
}
|
||||
|
||||
static void js_subghz_is_external(struct mjs* mjs) {
|
||||
mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);
|
||||
JsSubghzInst* js_subghz = mjs_get_ptr(mjs, obj_inst);
|
||||
furi_assert(js_subghz);
|
||||
|
||||
mjs_return(mjs, mjs_mk_boolean(mjs, js_subghz->is_external));
|
||||
}
|
||||
|
||||
static void js_subghz_set_frequency(struct mjs* mjs) {
|
||||
mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);
|
||||
JsSubghzInst* js_subghz = mjs_get_ptr(mjs, obj_inst);
|
||||
furi_assert(js_subghz);
|
||||
|
||||
if(js_subghz->state != JsSubghzRadioStateIDLE) {
|
||||
mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Radio is not in IDLE state");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t frequency;
|
||||
if(!get_int_arg(mjs, 0, &frequency)) return;
|
||||
|
||||
if(!subghz_devices_is_frequency_valid(js_subghz->radio_device, frequency)) {
|
||||
mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Invalid frequency");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
|
||||
js_subghz->frequency = subghz_devices_set_frequency(js_subghz->radio_device, frequency);
|
||||
|
||||
mjs_return(mjs, mjs_mk_number(mjs, (double)js_subghz->frequency));
|
||||
}
|
||||
|
||||
static void js_subghz_get_frequency(struct mjs* mjs) {
|
||||
mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);
|
||||
JsSubghzInst* js_subghz = mjs_get_ptr(mjs, obj_inst);
|
||||
furi_assert(js_subghz);
|
||||
|
||||
mjs_return(mjs, mjs_mk_number(mjs, (double)js_subghz->frequency));
|
||||
}
|
||||
|
||||
static void js_subghz_transmit_file(struct mjs* mjs) {
|
||||
mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);
|
||||
JsSubghzInst* js_subghz = mjs_get_ptr(mjs, obj_inst);
|
||||
furi_assert(js_subghz);
|
||||
|
||||
mjs_val_t file = mjs_arg(mjs, 0);
|
||||
|
||||
if(!mjs_is_string(file)) {
|
||||
mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "File must be a string");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
|
||||
const char* file_path = mjs_get_string(mjs, &file, NULL);
|
||||
if(!file_path) {
|
||||
mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Failed to get file path");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
FlipperFormat* fff_file = flipper_format_file_alloc(storage);
|
||||
FlipperFormat* fff_data_raw = flipper_format_string_alloc();
|
||||
|
||||
if(!flipper_format_file_open_existing(fff_file, file_path)) {
|
||||
flipper_format_free(fff_file);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Failed to open file");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
|
||||
SubGhzEnvironment* environment = subghz_environment_alloc();
|
||||
if(!subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_NAME)) {
|
||||
FURI_LOG_I(tag, "Load_keystore keeloq_mfcodes \033[0;31mERROR\033[0m\r\n");
|
||||
}
|
||||
if(!subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_USER_NAME)) {
|
||||
FURI_LOG_I(tag, "Load_keystore keeloq_mfcodes_user \033[0;33mAbsent\033[0m\r\n");
|
||||
}
|
||||
subghz_environment_set_alutech_at_4n_rainbow_table_file_name(
|
||||
environment, SUBGHZ_ALUTECH_AT_4N_DIR_NAME);
|
||||
subghz_environment_set_nice_flor_s_rainbow_table_file_name(
|
||||
environment, SUBGHZ_NICE_FLOR_S_DIR_NAME);
|
||||
subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry);
|
||||
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
SubGhzTransmitter* transmitter = NULL;
|
||||
bool is_init_protocol = true;
|
||||
bool is_sent = false;
|
||||
uint32_t frequency = 0;
|
||||
uint32_t repeat = 10;
|
||||
|
||||
do {
|
||||
//Load frequency
|
||||
if(!flipper_format_read_uint32(fff_file, "Frequency", &frequency, 1)) {
|
||||
mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Failed to read frequency from file");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
break;
|
||||
}
|
||||
|
||||
if(!subghz_devices_is_frequency_valid(js_subghz->radio_device, frequency)) {
|
||||
mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Invalid frequency");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
break;
|
||||
}
|
||||
|
||||
if(!flipper_format_read_string(fff_file, "Preset", temp_str)) {
|
||||
mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Failed to read preset from file");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
break;
|
||||
}
|
||||
|
||||
subghz_devices_reset(js_subghz->radio_device);
|
||||
|
||||
if(!strcmp(furi_string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) {
|
||||
mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Custom presets are not supported (yet)");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
break;
|
||||
} else {
|
||||
subghz_devices_load_preset(
|
||||
js_subghz->radio_device,
|
||||
js_subghz_get_preset_name(furi_string_get_cstr(temp_str)),
|
||||
NULL);
|
||||
}
|
||||
|
||||
js_subghz->frequency = subghz_devices_set_frequency(js_subghz->radio_device, frequency);
|
||||
|
||||
if(!flipper_format_read_string(fff_file, "Protocol", temp_str)) {
|
||||
mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Failed to read protocol from file");
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
break;
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus status;
|
||||
bool is_raw = false;
|
||||
|
||||
if(!strcmp(furi_string_get_cstr(temp_str), "RAW")) {
|
||||
subghz_protocol_raw_gen_fff_data(
|
||||
fff_data_raw, file_path, subghz_devices_get_name(js_subghz->radio_device));
|
||||
is_raw = true;
|
||||
}
|
||||
|
||||
transmitter = subghz_transmitter_alloc_init(environment, furi_string_get_cstr(temp_str));
|
||||
if(transmitter == NULL) {
|
||||
is_init_protocol = false;
|
||||
}
|
||||
|
||||
if(is_init_protocol) {
|
||||
status = subghz_transmitter_deserialize(transmitter, is_raw ? fff_data_raw : fff_file);
|
||||
if(status != SubGhzProtocolStatusOk) {
|
||||
FURI_LOG_I(tag, "failed to deserialize transmitter");
|
||||
is_init_protocol = false;
|
||||
}
|
||||
} else {
|
||||
FURI_LOG_I(tag, "failed to allocate transmitter");
|
||||
subghz_devices_idle(js_subghz->radio_device);
|
||||
js_subghz->state = JsSubghzRadioStateIDLE;
|
||||
}
|
||||
} while(false);
|
||||
|
||||
if(is_init_protocol) {
|
||||
if(!js_subghz->is_external) {
|
||||
furi_hal_power_suppress_charge_enter();
|
||||
}
|
||||
|
||||
FURI_LOG_I(tag, "transmitting file %s", file_path);
|
||||
|
||||
do {
|
||||
furi_delay_ms(200);
|
||||
if(subghz_devices_start_async_tx(
|
||||
js_subghz->radio_device, subghz_transmitter_yield, transmitter)) {
|
||||
while(!subghz_devices_is_async_complete_tx(js_subghz->radio_device)) {
|
||||
furi_delay_ms(333);
|
||||
}
|
||||
subghz_devices_stop_async_tx(js_subghz->radio_device);
|
||||
is_sent = true;
|
||||
} else {
|
||||
FURI_LOG_E(tag, "failed to start async tx");
|
||||
}
|
||||
|
||||
} while(repeat && !strcmp(furi_string_get_cstr(temp_str), "RAW"));
|
||||
|
||||
subghz_devices_idle(js_subghz->radio_device);
|
||||
js_subghz->state = JsSubghzRadioStateIDLE;
|
||||
|
||||
if(!js_subghz->is_external) {
|
||||
furi_hal_power_suppress_charge_exit();
|
||||
}
|
||||
}
|
||||
|
||||
furi_string_free(temp_str);
|
||||
flipper_format_free(fff_file);
|
||||
flipper_format_free(fff_data_raw);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
subghz_environment_reset_keeloq(environment);
|
||||
subghz_environment_free(environment);
|
||||
subghz_transmitter_free(transmitter);
|
||||
|
||||
mjs_return(mjs, mjs_mk_boolean(mjs, is_sent));
|
||||
}
|
||||
|
||||
static void js_subghz_setup(struct mjs* mjs) {
|
||||
mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);
|
||||
JsSubghzInst* js_subghz = mjs_get_ptr(mjs, obj_inst);
|
||||
furi_assert(js_subghz);
|
||||
|
||||
js_subghz->radio_device =
|
||||
radio_device_loader_set(js_subghz->radio_device, SubGhzRadioDeviceTypeExternalCC1101);
|
||||
|
||||
if(!subghz_devices_is_connect(js_subghz->radio_device)) {
|
||||
js_subghz->is_external = true;
|
||||
} else {
|
||||
js_subghz->is_external = false;
|
||||
}
|
||||
|
||||
js_subghz->state = JsSubghzRadioStateIDLE;
|
||||
js_subghz->frequency = 433920000;
|
||||
|
||||
subghz_devices_reset(js_subghz->radio_device);
|
||||
subghz_devices_idle(js_subghz->radio_device);
|
||||
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
}
|
||||
|
||||
static void* js_subghz_create(struct mjs* mjs, mjs_val_t* object) {
|
||||
JsSubghzInst* js_subghz = malloc(sizeof(JsSubghzInst));
|
||||
mjs_val_t subghz_obj = mjs_mk_object(mjs);
|
||||
|
||||
subghz_devices_init();
|
||||
|
||||
mjs_set(mjs, subghz_obj, INST_PROP_NAME, ~0, mjs_mk_foreign(mjs, js_subghz));
|
||||
mjs_set(mjs, subghz_obj, "setup", ~0, MJS_MK_FN(js_subghz_setup));
|
||||
mjs_set(mjs, subghz_obj, "setRx", ~0, MJS_MK_FN(js_subghz_set_rx));
|
||||
mjs_set(mjs, subghz_obj, "setIdle", ~0, MJS_MK_FN(js_subgjz_set_idle));
|
||||
mjs_set(mjs, subghz_obj, "getRssi", ~0, MJS_MK_FN(js_subghz_get_rssi));
|
||||
mjs_set(mjs, subghz_obj, "getState", ~0, MJS_MK_FN(js_subghz_get_state));
|
||||
mjs_set(mjs, subghz_obj, "getFrequency", ~0, MJS_MK_FN(js_subghz_get_frequency));
|
||||
mjs_set(mjs, subghz_obj, "setFrequency", ~0, MJS_MK_FN(js_subghz_set_frequency));
|
||||
mjs_set(mjs, subghz_obj, "isExternal", ~0, MJS_MK_FN(js_subghz_is_external));
|
||||
mjs_set(mjs, subghz_obj, "transmitFile", ~0, MJS_MK_FN(js_subghz_transmit_file));
|
||||
|
||||
*object = subghz_obj;
|
||||
|
||||
return js_subghz;
|
||||
}
|
||||
|
||||
static void js_subghz_destroy(void* inst) {
|
||||
JsSubghzInst* js_subghz = inst;
|
||||
|
||||
subghz_devices_deinit();
|
||||
|
||||
free(js_subghz);
|
||||
}
|
||||
|
||||
static const JsModuleDescriptor js_subghz_desc = {
|
||||
"subghz",
|
||||
js_subghz_create,
|
||||
js_subghz_destroy,
|
||||
};
|
||||
|
||||
static const FlipperAppPluginDescriptor plugin_descriptor = {
|
||||
.appid = PLUGIN_APP_ID,
|
||||
.ep_api_version = PLUGIN_API_VERSION,
|
||||
.entry_point = &js_subghz_desc,
|
||||
};
|
||||
|
||||
const FlipperAppPluginDescriptor* js_subghz_ep(void) {
|
||||
return &plugin_descriptor;
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
#include "radio_device_loader.h"
|
||||
|
||||
#include <applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h>
|
||||
#include <lib/subghz/devices/cc1101_int/cc1101_int_interconnect.h>
|
||||
|
||||
static void radio_device_loader_power_on() {
|
||||
uint8_t attempts = 0;
|
||||
while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) {
|
||||
furi_hal_power_enable_otg();
|
||||
//CC1101 power-up time
|
||||
furi_delay_ms(10);
|
||||
}
|
||||
}
|
||||
|
||||
static void radio_device_loader_power_off() {
|
||||
if(furi_hal_power_is_otg_enabled()) furi_hal_power_disable_otg();
|
||||
}
|
||||
|
||||
bool radio_device_loader_is_connect_external(const char* name) {
|
||||
bool is_connect = false;
|
||||
bool is_otg_enabled = furi_hal_power_is_otg_enabled();
|
||||
|
||||
if(!is_otg_enabled) {
|
||||
radio_device_loader_power_on();
|
||||
}
|
||||
|
||||
const SubGhzDevice* device = subghz_devices_get_by_name(name);
|
||||
if(device) {
|
||||
is_connect = subghz_devices_is_connect(device);
|
||||
}
|
||||
|
||||
if(!is_otg_enabled) {
|
||||
radio_device_loader_power_off();
|
||||
}
|
||||
return is_connect;
|
||||
}
|
||||
|
||||
const SubGhzDevice* radio_device_loader_set(
|
||||
const SubGhzDevice* current_radio_device,
|
||||
SubGhzRadioDeviceType radio_device_type) {
|
||||
const SubGhzDevice* radio_device;
|
||||
|
||||
if(radio_device_type == SubGhzRadioDeviceTypeExternalCC1101 &&
|
||||
radio_device_loader_is_connect_external(SUBGHZ_DEVICE_CC1101_EXT_NAME)) {
|
||||
radio_device_loader_power_on();
|
||||
radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_EXT_NAME);
|
||||
subghz_devices_begin(radio_device);
|
||||
} else if(current_radio_device == NULL) {
|
||||
radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME);
|
||||
} else {
|
||||
radio_device_loader_end(current_radio_device);
|
||||
radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME);
|
||||
}
|
||||
|
||||
return radio_device;
|
||||
}
|
||||
|
||||
void radio_device_loader_end(const SubGhzDevice* radio_device) {
|
||||
furi_assert(radio_device);
|
||||
radio_device_loader_power_off();
|
||||
if(radio_device != subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME)) {
|
||||
subghz_devices_end(radio_device);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include <lib/subghz/devices/devices.h>
|
||||
|
||||
/** SubGhzRadioDeviceType */
|
||||
typedef enum {
|
||||
SubGhzRadioDeviceTypeInternal,
|
||||
SubGhzRadioDeviceTypeExternalCC1101,
|
||||
} SubGhzRadioDeviceType;
|
||||
|
||||
const SubGhzDevice* radio_device_loader_set(
|
||||
const SubGhzDevice* current_radio_device,
|
||||
SubGhzRadioDeviceType radio_device_type);
|
||||
|
||||
void radio_device_loader_end(const SubGhzDevice* radio_device);
|
152
applications/system/js_app/modules/js_submenu.c
Normal file
152
applications/system/js_app/modules/js_submenu.c
Normal file
|
@ -0,0 +1,152 @@
|
|||
#include <gui/modules/submenu.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/view.h>
|
||||
#include "../js_modules.h"
|
||||
|
||||
typedef struct {
|
||||
Submenu* submenu;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
uint32_t result;
|
||||
} JsSubmenuInst;
|
||||
|
||||
typedef enum {
|
||||
JsSubmenuViewSubmenu,
|
||||
} JsSubmenuView;
|
||||
|
||||
static JsSubmenuInst* get_this_ctx(struct mjs* mjs) {
|
||||
mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);
|
||||
JsSubmenuInst* 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_str_arg(struct mjs* mjs, size_t index, const char** value) {
|
||||
mjs_val_t str_obj = mjs_arg(mjs, index);
|
||||
if(!mjs_is_string(str_obj)) {
|
||||
ret_bad_args(mjs, "Argument must be a string");
|
||||
return false;
|
||||
}
|
||||
size_t str_len = 0;
|
||||
*value = mjs_get_string(mjs, &str_obj, &str_len);
|
||||
if((str_len == 0) || (*value == NULL)) {
|
||||
ret_bad_args(mjs, "Bad string argument");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int32_t get_int_arg(struct mjs* mjs, size_t index, int32_t* value) {
|
||||
mjs_val_t int_obj = mjs_arg(mjs, index);
|
||||
if(!mjs_is_number(int_obj)) {
|
||||
ret_bad_args(mjs, "Argument must be a number");
|
||||
return false;
|
||||
}
|
||||
*value = mjs_get_int32(mjs, int_obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void submenu_callback(void* context, uint32_t id) {
|
||||
UNUSED(id);
|
||||
JsSubmenuInst* submenu = context;
|
||||
submenu->result = id;
|
||||
view_dispatcher_stop(submenu->view_dispatcher);
|
||||
}
|
||||
|
||||
static void js_submenu_add_item(struct mjs* mjs) {
|
||||
JsSubmenuInst* submenu = get_this_ctx(mjs);
|
||||
if(!check_arg_count(mjs, 2)) return;
|
||||
|
||||
const char* label;
|
||||
if(!get_str_arg(mjs, 0, &label)) return;
|
||||
|
||||
int32_t id;
|
||||
if(!get_int_arg(mjs, 1, &id)) return;
|
||||
|
||||
submenu_add_item(submenu->submenu, label, id, submenu_callback, submenu);
|
||||
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
}
|
||||
|
||||
static void js_submenu_set_header(struct mjs* mjs) {
|
||||
JsSubmenuInst* submenu = get_this_ctx(mjs);
|
||||
if(!check_arg_count(mjs, 1)) return;
|
||||
|
||||
const char* header;
|
||||
if(!get_str_arg(mjs, 0, &header)) return;
|
||||
|
||||
submenu_set_header(submenu->submenu, header);
|
||||
|
||||
mjs_return(mjs, MJS_UNDEFINED);
|
||||
}
|
||||
|
||||
static void js_submenu_show(struct mjs* mjs) {
|
||||
JsSubmenuInst* submenu = get_this_ctx(mjs);
|
||||
if(!check_arg_count(mjs, 0)) return;
|
||||
submenu->result = 0;
|
||||
|
||||
view_dispatcher_attach_to_gui(
|
||||
submenu->view_dispatcher, furi_record_open(RECORD_GUI), ViewDispatcherTypeFullscreen);
|
||||
furi_record_close(RECORD_GUI);
|
||||
|
||||
view_dispatcher_switch_to_view(submenu->view_dispatcher, JsSubmenuViewSubmenu);
|
||||
|
||||
view_dispatcher_run(submenu->view_dispatcher);
|
||||
|
||||
submenu_reset(submenu->submenu);
|
||||
|
||||
mjs_return(mjs, mjs_mk_number(mjs, submenu->result));
|
||||
}
|
||||
|
||||
static void* js_submenu_create(struct mjs* mjs, mjs_val_t* object) {
|
||||
JsSubmenuInst* submenu = malloc(sizeof(JsSubmenuInst));
|
||||
mjs_val_t submenu_obj = mjs_mk_object(mjs);
|
||||
mjs_set(mjs, submenu_obj, INST_PROP_NAME, ~0, mjs_mk_foreign(mjs, submenu));
|
||||
mjs_set(mjs, submenu_obj, "addItem", ~0, MJS_MK_FN(js_submenu_add_item));
|
||||
mjs_set(mjs, submenu_obj, "setHeader", ~0, MJS_MK_FN(js_submenu_set_header));
|
||||
mjs_set(mjs, submenu_obj, "show", ~0, MJS_MK_FN(js_submenu_show));
|
||||
submenu->submenu = submenu_alloc();
|
||||
submenu->view_dispatcher = view_dispatcher_alloc();
|
||||
view_dispatcher_enable_queue(submenu->view_dispatcher);
|
||||
view_dispatcher_add_view(
|
||||
submenu->view_dispatcher, JsSubmenuViewSubmenu, submenu_get_view(submenu->submenu));
|
||||
*object = submenu_obj;
|
||||
return submenu;
|
||||
}
|
||||
|
||||
static void js_submenu_destroy(void* inst) {
|
||||
JsSubmenuInst* submenu = inst;
|
||||
view_dispatcher_remove_view(submenu->view_dispatcher, JsSubmenuViewSubmenu);
|
||||
submenu_free(submenu->submenu);
|
||||
view_dispatcher_free(submenu->view_dispatcher);
|
||||
free(submenu);
|
||||
}
|
||||
|
||||
static const JsModuleDescriptor js_submenu_desc = {
|
||||
"submenu",
|
||||
js_submenu_create,
|
||||
js_submenu_destroy,
|
||||
};
|
||||
|
||||
static const FlipperAppPluginDescriptor submenu_plugin_descriptor = {
|
||||
.appid = PLUGIN_APP_ID,
|
||||
.ep_api_version = PLUGIN_API_VERSION,
|
||||
.entry_point = &js_submenu_desc,
|
||||
};
|
||||
|
||||
const FlipperAppPluginDescriptor* js_submenu_ep(void) {
|
||||
return &submenu_plugin_descriptor;
|
||||
}
|
|
@ -991,6 +991,7 @@ Function,-,exp2f,float,float
|
|||
Function,-,exp2l,long double,long double
|
||||
Function,+,expansion_disable,void,Expansion*
|
||||
Function,+,expansion_enable,void,Expansion*
|
||||
Function,+,expansion_is_connected,_Bool,Expansion*
|
||||
Function,+,expansion_set_listen_serial,void,"Expansion*, FuriHalSerialId"
|
||||
Function,-,expf,float,float
|
||||
Function,-,expl,long double,long double
|
||||
|
|
|
Loading…
Reference in a new issue