mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-05-12 10:58:36 -07:00
Merge branch 'dev' of https://github.com/Next-Flip/Momentum-Firmware into dev
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @file furi_hal_subghz.h
|
||||
* SubGhz HAL API
|
||||
* @file cc1101_ext.h
|
||||
* @brief External CC1101 transceiver access API.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
# Apps Assets folder Example
|
||||
# Apps Assets folder Example {#example_app_assets}
|
||||
|
||||
This example shows how to use the Apps Assets folder to store data that is not part of the application itself, but is required for its operation, and that data is provided with the application.
|
||||
|
||||
## Source code
|
||||
|
||||
Source code for this example can be found [here](https://github.com/flipperdevices/flipperzero-firmware/tree/dev/applications/examples/example_apps_assets).
|
||||
|
||||
## What is the Apps Assets Folder?
|
||||
|
||||
The **Apps Assets** folder is a folder where external applications unpack their assets.
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* @file example_apps_assets.c
|
||||
* @brief Application assets example.
|
||||
*/
|
||||
#include <furi.h>
|
||||
#include <storage/storage.h>
|
||||
#include <toolbox/stream/stream.h>
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
# Apps Data folder Example
|
||||
# Apps Data folder Example {#example_app_data}
|
||||
|
||||
This example demonstrates how to utilize the Apps Data folder to store data that is not part of the app itself, such as user data, configuration files, and so forth.
|
||||
|
||||
## Source code
|
||||
|
||||
Source code for this example can be found [here](https://github.com/flipperdevices/flipperzero-firmware/tree/dev/applications/examples/example_apps_data).
|
||||
|
||||
## What is the Apps Data Folder?
|
||||
|
||||
The **Apps Data** folder is a folder used to store data for external apps that are not part of the main firmware.
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* @file example_apps_data.c
|
||||
* @brief Application data example.
|
||||
*/
|
||||
#include <furi.h>
|
||||
#include <storage/storage.h>
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* @file ble_beacon_app.h
|
||||
* @brief BLE beacon example.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "extra_beacon.h"
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* @file example_custom_font.c
|
||||
* @brief Custom font example.
|
||||
*/
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
|
||||
@@ -1,11 +1,21 @@
|
||||
# Application icons
|
||||
# Application icons {#example_app_images}
|
||||
|
||||
## Source code
|
||||
|
||||
Source code for this example can be found [here](https://github.com/flipperdevices/flipperzero-firmware/tree/dev/applications/examples/example_images).
|
||||
|
||||
## General principle
|
||||
|
||||
To use icons, do the following:
|
||||
* add a line to the application manifest: `fap_icon_assets="folder"`, where `folder` points to the folder where your icons are located
|
||||
* add `#include "application_id_icons.h"` to the application code, where `application_id` is the appid from the manifest
|
||||
* every icon in the folder will be available as a `I_icon_name` variable, where `icon_name` is the name of the icon file without the extension
|
||||
|
||||
* Add a line to the application manifest: `fap_icon_assets="folder"`, where `folder` points to the folder where your icons are located
|
||||
* Add `#include "application_id_icons.h"` to the application code, where `application_id` is the appid from the manifest
|
||||
* Every icon in the folder will be available as a `I_icon_name` variable, where `icon_name` is the name of the icon file without the extension
|
||||
|
||||
## Example
|
||||
|
||||
We have an application with the following manifest:
|
||||
|
||||
```
|
||||
App(
|
||||
appid="example_images",
|
||||
@@ -17,6 +27,7 @@ App(
|
||||
So the icons are in the `images` folder and will be available in the generated `example_images_icons.h` file.
|
||||
|
||||
The example code is located in `example_images_main.c` and contains the following line:
|
||||
|
||||
```
|
||||
#include "example_images_icons.h"
|
||||
```
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* @file example_images.c
|
||||
* @brief Custom images example.
|
||||
*/
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
/*
|
||||
* An example of a plugin host application.
|
||||
/**
|
||||
* @file example_plugins.c
|
||||
* @brief Plugin host application example.
|
||||
*
|
||||
* Loads a single plugin and calls its methods.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
/*
|
||||
* An example of an advanced plugin host application.
|
||||
/**
|
||||
* @file example_plugins_multi.c
|
||||
* @brief Advanced plugin host application example.
|
||||
*
|
||||
* It uses PluginManager to load all plugins from a directory
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
/* A simple plugin implementing example_plugins application's plugin interface */
|
||||
/**
|
||||
* @file plugin1.c
|
||||
* @brief Plugin example 1.
|
||||
*
|
||||
* A simple plugin implementing example_plugins application's plugin interface
|
||||
*/
|
||||
|
||||
#include "plugin_interface.h"
|
||||
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
/* Second plugin implementing example_plugins application's plugin interface */
|
||||
/**
|
||||
* @file plugin2.c
|
||||
* @brief Plugin example 2.
|
||||
*
|
||||
* Second plugin implementing example_plugins application's plugin interface
|
||||
*/
|
||||
|
||||
#include "plugin_interface.h"
|
||||
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
/**
|
||||
* @file plugin_interface.h
|
||||
* @brief Example plugin interface.
|
||||
*
|
||||
* Common interface between a plugin and host application
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/* Common interface between a plugin and host application */
|
||||
|
||||
#define PLUGIN_APP_ID "example_plugins"
|
||||
#define PLUGIN_API_VERSION 1
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
/**
|
||||
* @file app_api.h
|
||||
* @brief Application API example.
|
||||
*
|
||||
* This file contains an API that is internally implemented by the application
|
||||
* It is also exposed to plugins to allow them to use the application's API.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
/*
|
||||
/**
|
||||
* @file plugin1.c
|
||||
* @brief Plugin example 1.
|
||||
*
|
||||
* This plugin uses both firmware's API interface and private application headers.
|
||||
* It can be loaded by a plugin manager that uses CompoundApiInterface,
|
||||
* which combines both interfaces.
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
/*
|
||||
/**
|
||||
* @file plugin2.c
|
||||
* @brief Plugin example 2.
|
||||
*
|
||||
* This plugin uses both firmware's API interface and private application headers.
|
||||
* It can be loaded by a plugin manager that uses CompoundApiInterface,
|
||||
* which combines both interfaces.
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
/**
|
||||
* @file plugin_interface.h
|
||||
* @brief Example plugin interface.
|
||||
*
|
||||
* Common interface between a plugin and host application
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/* Common interface between a plugin and host application */
|
||||
|
||||
#define PLUGIN_APP_ID "example_plugins_advanced"
|
||||
#define PLUGIN_API_VERSION 1
|
||||
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
# 1-Wire Thermometer
|
||||
# 1-Wire Thermometer {#example_thermo}
|
||||
|
||||
This example application demonstrates the use of the 1-Wire library with a DS18B20 thermometer.
|
||||
It also covers basic GUI, input handling, threads and localisation.
|
||||
|
||||
## Source code
|
||||
|
||||
Source code for this example can be found [here](https://github.com/flipperdevices/flipperzero-firmware/tree/dev/applications/examples/example_thermo).
|
||||
|
||||
## Electrical connections
|
||||
|
||||
Before launching the application, connect the sensor to Flipper's external GPIO according to the table below:
|
||||
| DS18B20 | Flipper |
|
||||
| :-----: | :-----: |
|
||||
@@ -15,12 +21,14 @@ Before launching the application, connect the sensor to Flipper's external GPIO
|
||||
*NOTE 2*: For any other pin than 17, connect an external 4.7k pull-up resistor to pin 9.
|
||||
|
||||
## Launching the application
|
||||
|
||||
In order to launch this demo, follow the steps below:
|
||||
1. Make sure your Flipper has an SD card installed.
|
||||
2. Connect your Flipper to the computer via a USB cable.
|
||||
3. Run `./fbt launch APPSRC=example_thermo` in your terminal emulator of choice.
|
||||
|
||||
## Changing the data pin
|
||||
|
||||
It is possible to use other GPIO pin as a 1-Wire data pin. In order to change it, set the `THERMO_GPIO_PIN` macro to any of the options listed below:
|
||||
|
||||
```c
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
/*
|
||||
/**
|
||||
* @file example_thermo.c
|
||||
* @brief 1-Wire thermometer example.
|
||||
*
|
||||
* This file contains an example application that reads and displays
|
||||
* the temperature from a DS18B20 1-wire thermometer.
|
||||
*
|
||||
|
||||
Submodule applications/external updated: d2f94be5ef...3da3a125fe
@@ -1,11 +1,13 @@
|
||||
#include "nfc_supported_card_plugin.h"
|
||||
#include <flipper_application.h>
|
||||
#include <core/check.h>
|
||||
|
||||
#include <flipper_application/flipper_application.h>
|
||||
|
||||
#include <nfc/nfc_device.h>
|
||||
#include <bit_lib/bit_lib.h>
|
||||
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
|
||||
#include "../../api/mosgortrans/mosgortrans_util.h"
|
||||
|
||||
#include <bit_lib.h>
|
||||
#include <datetime.h>
|
||||
#include "furi_hal_rtc.h"
|
||||
|
||||
#define TAG "Troika"
|
||||
|
||||
@@ -88,7 +90,7 @@ static bool troika_get_card_config(TroikaCardConfig* config, MfClassicType type)
|
||||
config->data_sector = 11;
|
||||
config->keys = troika_1k_keys;
|
||||
} else if(type == MfClassicType4k) {
|
||||
config->data_sector = 11;
|
||||
config->data_sector = 8; // Further testing needed
|
||||
config->keys = troika_4k_keys;
|
||||
} else {
|
||||
success = false;
|
||||
@@ -244,4 +246,4 @@ static const FlipperAppPluginDescriptor troika_plugin_descriptor = {
|
||||
/* Plugin entry point - must return a pointer to const descriptor */
|
||||
const FlipperAppPluginDescriptor* troika_plugin_ep() {
|
||||
return &troika_plugin_descriptor;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,7 +248,6 @@ void canvas_draw_bitmap(
|
||||
* @param x x coordinate
|
||||
* @param y y coordinate
|
||||
* @param icon Icon instance
|
||||
* @param flip IconFlip
|
||||
* @param rotation IconRotation
|
||||
*/
|
||||
void canvas_draw_icon_ex(
|
||||
|
||||
@@ -107,7 +107,7 @@ CanvasOrientation canvas_get_orientation(const Canvas* canvas);
|
||||
|
||||
/** Draw a u8g2 bitmap
|
||||
*
|
||||
* @param canvas Canvas instance
|
||||
* @param u8g2 u8g2 instance
|
||||
* @param x x coordinate
|
||||
* @param y y coordinate
|
||||
* @param width width
|
||||
|
||||
@@ -205,8 +205,7 @@ void elements_bubble(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_
|
||||
* @param canvas Canvas instance
|
||||
* @param x left x coordinates
|
||||
* @param y top y coordinate
|
||||
* @param width bubble width
|
||||
* @param height bubble height
|
||||
* @param text text to display
|
||||
* @param horizontal horizontal aligning
|
||||
* @param vertical aligning
|
||||
*/
|
||||
|
||||
@@ -114,7 +114,6 @@ void dialog_ex_set_text(
|
||||
* @param x x position
|
||||
* @param y y position
|
||||
* @param icon The icon
|
||||
* @param name icon to be shown
|
||||
*/
|
||||
void dialog_ex_set_icon(DialogEx* dialog_ex, uint8_t x, uint8_t y, const Icon* icon);
|
||||
|
||||
|
||||
@@ -108,7 +108,6 @@ bool scene_manager_handle_back_event(SceneManager* scene_manager);
|
||||
* Calls Scene event handler with Tick event parameter
|
||||
*
|
||||
* @param scene_manager SceneManager instance
|
||||
* @return true if event was consumed, false otherwise
|
||||
*/
|
||||
void scene_manager_handle_tick_event(SceneManager* scene_manager);
|
||||
|
||||
|
||||
@@ -34,15 +34,15 @@ typedef enum {
|
||||
typedef struct View View;
|
||||
|
||||
/** View Draw callback
|
||||
* @param canvas, pointer to canvas
|
||||
* @param view_model, pointer to context
|
||||
* @param canvas pointer to canvas
|
||||
* @param model pointer to model
|
||||
* @warning called from GUI thread
|
||||
*/
|
||||
typedef void (*ViewDrawCallback)(Canvas* canvas, void* model);
|
||||
|
||||
/** View Input callback
|
||||
* @param event, pointer to input event data
|
||||
* @param context, pointer to context
|
||||
* @param event pointer to input event data
|
||||
* @param context pointer to context
|
||||
* @return true if event handled, false if event ignored
|
||||
* @warning called from GUI thread
|
||||
*/
|
||||
@@ -57,29 +57,29 @@ typedef bool (*ViewInputCallback)(InputEvent* event, void* context);
|
||||
typedef bool (*ViewAsciiCallback)(AsciiEvent* event, void* context);
|
||||
|
||||
/** View Custom callback
|
||||
* @param event, number of custom event
|
||||
* @param context, pointer to context
|
||||
* @param event number of custom event
|
||||
* @param context pointer to context
|
||||
* @return true if event handled, false if event ignored
|
||||
*/
|
||||
typedef bool (*ViewCustomCallback)(uint32_t event, void* context);
|
||||
|
||||
/** View navigation callback
|
||||
* @param context, pointer to context
|
||||
* @param context pointer to context
|
||||
* @return next view id
|
||||
* @warning called from GUI thread
|
||||
*/
|
||||
typedef uint32_t (*ViewNavigationCallback)(void* context);
|
||||
|
||||
/** View callback
|
||||
* @param context, pointer to context
|
||||
* @param context pointer to context
|
||||
* @warning called from GUI thread
|
||||
*/
|
||||
typedef void (*ViewCallback)(void* context);
|
||||
|
||||
/** View Update Callback Called upon model change, need to be propagated to GUI
|
||||
* throw ViewPort update
|
||||
* @param view, pointer to view
|
||||
* @param context, pointer to context
|
||||
* @param view pointer to view
|
||||
* @param context pointer to context
|
||||
* @warning called from GUI thread
|
||||
*/
|
||||
typedef void (*ViewUpdateCallback)(View* view, void* context);
|
||||
|
||||
@@ -44,7 +44,7 @@ View* view_stack_get_view(ViewStack* view_stack);
|
||||
* Adds View on top of ViewStack.
|
||||
*
|
||||
* @param view_stack instance
|
||||
* @view view view to add
|
||||
* @param view view to add
|
||||
*/
|
||||
void view_stack_add_view(ViewStack* view_stack, View* view);
|
||||
|
||||
@@ -52,7 +52,7 @@ void view_stack_add_view(ViewStack* view_stack, View* view);
|
||||
* If no View to remove found - ignore.
|
||||
*
|
||||
* @param view_stack instance
|
||||
* @view view view to remove
|
||||
* @param view view to remove
|
||||
*/
|
||||
void view_stack_remove_view(ViewStack* view_stack, View* view);
|
||||
|
||||
|
||||
@@ -368,7 +368,7 @@ FS_Error storage_common_merge(Storage* storage, const char* old_path, const char
|
||||
* @brief Create a directory.
|
||||
*
|
||||
* @param storage pointer to a storage API instance.
|
||||
* @param fs_path pointer to a zero-terminated string containing the directory path.
|
||||
* @param path pointer to a zero-terminated string containing the directory path.
|
||||
* @return FSE_OK if the directory has been successfully created, any other error code on failure.
|
||||
*/
|
||||
FS_Error storage_common_mkdir(Storage* storage, const char* path);
|
||||
@@ -395,7 +395,6 @@ FS_Error storage_common_fs_info(
|
||||
*
|
||||
* @param storage pointer to a storage API instance.
|
||||
* @param path pointer to a zero-terminated string containing the path in question.
|
||||
* @return true if the path was successfully resolved, false otherwise.
|
||||
*/
|
||||
void storage_common_resolve_path_and_ensure_app_directory(Storage* storage, FuriString* path);
|
||||
|
||||
@@ -555,7 +554,8 @@ FS_Error storage_int_backup(Storage* storage, const char* dstname);
|
||||
* @param converter pointer to a filename conversion function (may be NULL).
|
||||
* @return FSE_OK if the storage was successfully restored, any other error code on failure.
|
||||
*/
|
||||
FS_Error storage_int_restore(Storage* api, const char* dstname, Storage_name_converter converter);
|
||||
FS_Error
|
||||
storage_int_restore(Storage* storage, const char* dstname, Storage_name_converter converter);
|
||||
|
||||
/******************* FatFs Virtual Mount Functions *******************/
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
#include "storage.h"
|
||||
#include <toolbox/tar/tar_archive.h>
|
||||
|
||||
FS_Error storage_int_backup(Storage* api, const char* dstname) {
|
||||
TarArchive* archive = tar_archive_alloc(api);
|
||||
FS_Error storage_int_backup(Storage* storage, const char* dstname) {
|
||||
TarArchive* archive = tar_archive_alloc(storage);
|
||||
bool success = tar_archive_open(archive, dstname, TAR_OPEN_MODE_WRITE) &&
|
||||
tar_archive_add_dir(archive, STORAGE_INT_PATH_PREFIX, "") &&
|
||||
tar_archive_finalize(archive);
|
||||
@@ -11,8 +11,9 @@ FS_Error storage_int_backup(Storage* api, const char* dstname) {
|
||||
return success ? FSE_OK : FSE_INTERNAL;
|
||||
}
|
||||
|
||||
FS_Error storage_int_restore(Storage* api, const char* srcname, Storage_name_converter converter) {
|
||||
TarArchive* archive = tar_archive_alloc(api);
|
||||
FS_Error
|
||||
storage_int_restore(Storage* storage, const char* srcname, Storage_name_converter converter) {
|
||||
TarArchive* archive = tar_archive_alloc(storage);
|
||||
bool success = tar_archive_open(archive, srcname, TAR_OPEN_MODE_READ) &&
|
||||
tar_archive_unpack_to(archive, STORAGE_INT_PATH_PREFIX, converter);
|
||||
tar_archive_free(archive);
|
||||
|
||||
@@ -71,6 +71,29 @@ 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,
|
||||
|
||||
@@ -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);
|
||||
@@ -8,4 +8,4 @@ submenu.setHeader("Select an option:");
|
||||
|
||||
let result = submenu.show();
|
||||
|
||||
print("Result: ", result);
|
||||
print("Result:", result);
|
||||
|
||||
242
applications/system/js_app/modules/js_blebeacon.c
Normal file
242
applications/system/js_app/modules/js_blebeacon.c
Normal file
@@ -0,0 +1,242 @@
|
||||
#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();
|
||||
}
|
||||
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, "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_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;
|
||||
}
|
||||
@@ -9,6 +9,10 @@ typedef struct {
|
||||
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);
|
||||
@@ -98,7 +102,7 @@ static void js_submenu_show(struct mjs* mjs) {
|
||||
submenu->view_dispatcher, furi_record_open(RECORD_GUI), ViewDispatcherTypeFullscreen);
|
||||
furi_record_close(RECORD_GUI);
|
||||
|
||||
view_dispatcher_switch_to_view(submenu->view_dispatcher, 0);
|
||||
view_dispatcher_switch_to_view(submenu->view_dispatcher, JsSubmenuViewSubmenu);
|
||||
|
||||
view_dispatcher_run(submenu->view_dispatcher);
|
||||
|
||||
@@ -117,13 +121,15 @@ static void* js_submenu_create(struct mjs* mjs, mjs_val_t* object) {
|
||||
submenu->submenu = submenu_alloc();
|
||||
submenu->view_dispatcher = view_dispatcher_alloc();
|
||||
view_dispatcher_enable_queue(submenu->view_dispatcher);
|
||||
view_dispatcher_add_view(submenu->view_dispatcher, 0, submenu_get_view(submenu->submenu));
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user