1
0
Fork 0

Merge branch 'espruino:master' into master

master
shansou504 2023-12-10 23:50:39 -05:00 committed by GitHub
commit 0375147ee5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 295 additions and 28 deletions

1
apps/bad/ChangeLog Normal file
View File

@ -0,0 +1 @@
0.01: attempt to import

8
apps/bad/README.md Normal file
View File

@ -0,0 +1,8 @@
# Bad Apple demo ![](app.png)
"Bad Apple" is like "Hello World" for graphics system. So this is it
for Bangle.js2. Watch have no speaker, so vibration motor is used,
instead, to produce (pretty quiet) sound.
Tools for preparing bad.araw and bad.vraw are in prep/ directory. Full
3 minute demo should actually fit to the watch.

1
apps/bad/app-icon.js Normal file
View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwgIifiAFWj//Aod///gAgMH///+AFBn4FB/AQDAoYEB//8gEBAokDAoX+ApguCAAIFqGoYFNLIZHBApZxFAoyDCAoqJCSoqsBUIcPAoKtF4AFBJAS/DHIQAeA="))

BIN
apps/bad/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

65
apps/bad/bad.app.js Normal file
View File

@ -0,0 +1,65 @@
/* sox Rear_Right.wav -r 4k -b 8 -c 1 -e unsigned-integer 0.raw vol 2
aplay -r 4000 /tmp/0.raw
*/
/* https://forum.espruino.com/conversations/348912/ */
let pin = D19;
function play(name, callback) {
function playChar(offs) {
var l = 10240;
var s = require("Storage").read(name, offs, l);
//print("Waveform " + name + " " + s.length);
if (!s.length) {
digitalWrite(pin,0);
if (callback) callback();
return;
}
var w = new Waveform(s.length);
var b = w.buffer;
b.set(s);
//print("Buffer", s.length);
//for (var i=s.length-1;i>=0;i--)b[i]/=4;
w.startOutput(pin, 4000);
w.on("finish", function(buf) {
playChar(offs+l);
});
}
analogWrite(pin, 0.1, {freq:40000});
playChar(0);
}
function video(name, callback) {
function frame() {
var s = require("Storage").read(name, offs, l);
if (!s)
return;
g.drawImage(s, 0, 0, { scale: 2 });
g.flip();
offs += l;
}
g.clear();
var offs = 0;
//var l = 3875; for 176x176
//var l = 515; for 64x64
var l = 971;
setInterval(frame, 200);
}
function run() {
clearInterval(i);
print("Running");
play('bad.araw');
t1 = getTime();
video('bad.vraw');
print("100 frames in ", getTime()-t1);
// 1.7s, unscaled
// 2.68s, scale 1.01
// 5.73s, scale 2.00
// 9.93s, scale 2, full screen
// 14.4s scaled. 176/64
}
print("Loaded");
i = setInterval(run, 100);

BIN
apps/bad/bad.araw Normal file

Binary file not shown.

BIN
apps/bad/bad.vraw Normal file

Binary file not shown.

16
apps/bad/metadata.json Normal file
View File

@ -0,0 +1,16 @@
{ "id": "bad",
"name": "Bad Apple",
"version":"0.01",
"description": "Bad Apple demo",
"icon": "app.png",
"readme": "README.md",
"supports" : ["BANGLEJS2"],
"allow_emulator": false,
"tags": "game",
"storage": [
{"name":"bad.app.js","url":"bad.app.js"},
{"name":"bad.vraw","url":"bad.vraw"},
{"name":"bad.araw","url":"bad.araw"},
{"name":"bad.img","url":"app-icon.js","evaluate":true}
]
}

32
apps/bad/prep/img_convert.py Executable file
View File

@ -0,0 +1,32 @@
#!/usr/bin/python3
from PIL import Image
import os
def convert_image(input_path, output_width, output_height):
img = Image.open(input_path)
img_resized = img.resize((output_width, output_height), Image.ANTIALIAS)
img_gray = img_resized.convert('L')
img_1bpp = img_gray.point(lambda x: 0 if x < 128 else 255, '1')
return img_1bpp
def convert_and_append_header(input_directory, size):
input_files = [f for f in os.listdir(input_directory) if f.startswith("image_") and f.endswith(".png")]
input_files.sort()
header_bytes = size.to_bytes(1, byteorder='big') + size.to_bytes(1, byteorder='big') + b'\x01'
for i, input_file in enumerate(input_files):
input_path = os.path.join(input_directory, input_file)
img_1bpp = convert_image(input_path, size, size)
output_file = input_path + ".raw"
with open(output_file, 'wb') as raw_file:
raw_file.write(header_bytes)
raw_file.write(img_1bpp.tobytes())
if __name__ == "__main__":
input_directory = "." # Replace with the path to your image directory
output_width = 88
output_file_path = "output_with_header.raw" # Replace with the desired output file path
convert_and_append_header(input_directory, output_width)

20
apps/bad/prep/run Executable file
View File

@ -0,0 +1,20 @@
#!/bin/bash
# aplay -r 4000 /tmp/0.raw
#bug: Terminal exists on b.js, it is dumb terminal, not vt100.
rm image_*.png image_*.png.raw output.wav ../bad.araw ../bad.vraw
I=bad.mp4
S=1:18
E=1:50
ffmpeg -i $I -ss $S -to $E -vn -acodec pcm_u8 -ar 4000 -ac 1 -y output.wav
./wav_divider.py
mv output.raw ../bad.araw
ffmpeg -i $I -ss $S -to $E -r 5 -vf fps=5 image_%04d.png
./img_convert.py
cat *.png.raw > ../bad.vraw
ls -al ../bad.*

13
apps/bad/prep/wav_divider.py Executable file
View File

@ -0,0 +1,13 @@
#!/usr/bin/python3
def divide_bytes(input_file, output_file):
with open(input_file, 'rb') as infile:
with open(output_file, 'wb') as outfile:
byte = infile.read(1)
while byte:
# Convert byte to integer, divide by 4, and write back as byte
new_byte = bytes([int.from_bytes(byte, byteorder='big') // 3])
outfile.write(new_byte)
byte = infile.read(1)
divide_bytes("output.wav", "output.raw")

View File

@ -32,12 +32,14 @@ function readFile(input) {
const jCalData = ICAL.parse(icalText);
const comp = new ICAL.Component(jCalData);
const vtz = comp.getFirstSubcomponent('vtimezone');
const tz = new ICAL.Timezone(vtz);
const tz = vtz != null ? new ICAL.Timezone(vtz) : null;
// Fetch the VEVENT part
comp.getAllSubcomponents('vevent').forEach(vevent => {
const event = new ICAL.Event(vevent);
if (tz != null) {
event.startDate.zone = tz;
}
holidays = holidays.filter(holiday => !sameDay(new Date(holiday.date), event.startDate.toJSDate())); // remove if already exists
const holiday = eventToHoliday(event);

View File

@ -27,3 +27,4 @@
0.27: Report latest HRM rather than HRM 10 minutes ago (fix #2395)
0.28: Battery Vref implemented correctly.
0.29: Support fastload.
0.30: Add many new colors to the settings and allows random colors on startup if enabled.

View File

@ -19,6 +19,7 @@ the "sched" app must be installed on your device.
* The lower orange line indicates the battery level.
* Display graphs (day or month) for steps + hrm on the second screen.
* Customizable theming colors in the settings menu of the app.
* Allow random colors on startup.
* Enable or disable the alarm feature.
* Enable or disbale the graphs for steps + hrm.

View File

@ -16,18 +16,36 @@ let settings = {
themeColor3BG: "#0094FF",
disableAlarms: false,
disableData: false,
randomColors: false,
};
let saved_settings = storage.readJSON(SETTINGS_FILE, 1) || settings;
for (const key in saved_settings) {
settings[key] = saved_settings[key];
}
/*
* Colors to use
*/
let color1 = settings.themeColor3BG;
let color2 = settings.themeColor1BG;
let color3 = settings.themeColor2BG;
//Colors to use
var color_options = [
'Green', 'Orange', 'Cyan', 'Purple', 'Red', 'Blue', 'Yellow', 'White',
'Purple', 'Pink', 'Light Green', 'Brown', 'Turquoise', 'Magenta', 'Lime',
'Gold', 'Sky Blue', 'Rose', 'Lavender', 'Amber', 'Indigo', 'Teal',
'Crimson', 'Maroon', 'Firebrick', 'Dark Red', 'Aqua', 'Emerald', 'Royal Blue',
'Sunset Orange', 'Turquoise Blue', 'Hot Pink', 'Goldenrod', 'Deep Sky Blue'
];
var bg_code = [
'#00ff00', '#FF9900', '#0094FF', '#FF00DC', '#ff0000', '#0000ff', '#ffef00', '#FFFFFF',
'#FF00FF', '#6C00FF', '#99FF00', '#8B4513', '#40E0D0', '#FF00FF', '#00FF00', '#FFD700',
'#87CEEB', '#FF007F', '#E6E6FA', '#FFBF00', '#4B0082', '#008080', '#DC143C', '#800000',
'#B22222', '#8B0000', '#00FFFF', '#008000', '#4169E1', '#FF4500', '#40E0D0', '#FF69B4',
'#DAA520', '#00BFFF'
];
let color1;
let color2;
let color3;
let cWhite = "#FFFFFF";
let cBlack = "#000000";
let cGrey = "#424242";
@ -58,10 +76,77 @@ let convert24to16 = function(input)
return "0x"+RGB565.toString(16);
};
let color1C = convert24to16(color1);//Converting colors to the correct format.
let color2C = convert24to16(color2);
let color3C = convert24to16(color3);
//Converting colors to the correct format.
let randomColors = function () {
if (settings.randomColors) {
do {
color1 = getRandomColor();
color2 = getRandomColor();
color3 = getRandomColor();
} while (!areColorsDistinct(color1, color2, color3));
} else {
color1 = settings.themeColor3BG;
color2 = settings.themeColor1BG;
color3 = settings.themeColor2BG;
}
// Converting colors to the correct format.
color1C = convert24to16(color1);
color2C = convert24to16(color2);
color3C = convert24to16(color3);
};
// Function to get a random color from the bg_code array.
let getRandomColor = function () {
return bg_code[Math.floor(Math.random() * bg_code.length)];
};
// Function to check if three colors are distinct enough.
let areColorsDistinct = function (color1, color2, color3) {
return (
color1 !== color2 &&
color2 !== color3 &&
color1 !== color3 &&
hasSufficientContrast(color1, color2) &&
hasSufficientContrast(color2, color3) &&
hasSufficientContrast(color1, color3)
);
};
// Function to calculate contrast between two colors.
let hasSufficientContrast = function (color1, color2) {
const contrastThreshold = 0.10; // Adjust this threshold based on your preference.
// Calculate the luminance values (for simplicity, assuming sRGB color space).
const luminance1 = getLuminance(color1);
const luminance2 = getLuminance(color2);
// Calculate the contrast ratio.
const contrastRatio = (Math.max(luminance1, luminance2) + 0.05) / (Math.min(luminance1, luminance2) + 0.05);
// Check if the contrast ratio meets the threshold.
return contrastRatio >= contrastThreshold;
};
// Function to calculate luminance from a hex color.
let getLuminance = function (hexColor) {
const rgb = hexToRgb(hexColor);
return 0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b;
};
// Function to convert hex color to RGB.
let hexToRgb = function (hex) {
const bigint = parseInt(hex.slice(1), 16);
const r = (bigint >> 16) & 255;
const g = (bigint >> 8) & 255;
const b = bigint & 255;
return { r, g, b };
};
randomColors();//Apply random colors if applied
/*
* Requirements and globals
*/
@ -224,7 +309,7 @@ let drawData = function(key, y, c){
let _drawData = function(key, y, c){
key = key.toUpperCase()
key = key.toUpperCase();
let text = key;
let value = "ERR";
let should_print= true;
@ -271,7 +356,7 @@ let _drawData = function(key, y, c){
value = Math.round(data.altitude);
printRow(text, value, y, c);
}
})
});
} else if(key == "CORET"){
value = locale.temp(parseInt(E.getTemperature()));
@ -374,7 +459,7 @@ let drawPosition0 = function(){
drawHorizontalBgLine(color2, batStart, batX2, 171, 5);
drawHorizontalBgLine(cGrey, batX2, 172, 171, 5);
for(let i=0; i+batStart<=172; i+=parseInt(batWidth/4)){
drawHorizontalBgLine(cBlack, batStart+i, batStart+i+3, 168, 8)
drawHorizontalBgLine(cBlack, batStart+i, batStart+i+3, 168, 8);
}
// Draw Infos
@ -603,7 +688,7 @@ let getWeather = function(){
let speedFactor = settings.speed == "kph" ? 1.0 : 1.0 / 1.60934;
weather.wind = Math.round(wind[1] * speedFactor);
return weather
return weather;
} catch(ex) {
// Return default
@ -649,7 +734,7 @@ let getAlarmMinutes = function(){
let increaseAlarm = function(){
try{
let minutes = isAlarmEnabled() ? getAlarmMinutes() : 0;
let alarm = require('sched')
let alarm = require('sched');
alarm.setAlarm(TIMER_IDX, {
timer : (minutes+5)*60*1000,
});
@ -662,7 +747,7 @@ let decreaseAlarm = function(){
let minutes = getAlarmMinutes();
minutes -= 5;
let alarm = require('sched')
let alarm = require('sched');
alarm.setAlarm(TIMER_IDX, undefined);
if(minutes > 0){
@ -773,7 +858,6 @@ Bangle.setUI({mode:"clock",remove:function() {
widget_utils.cleanup();
}});
Bangle.loadWidgets();
// Clear the screen once, at startup and draw clock
g.setTheme({bg:"#000",fg:"#fff",dark:true}).clear();
draw();

View File

@ -15,6 +15,7 @@
themeColor3BG: "#0094FF",
disableAlarms: false,
disableData: false,
randomColors: false,
};
let saved_settings = storage.readJSON(SETTINGS_FILE, 1) || settings;
for (const key in saved_settings) {
@ -28,9 +29,21 @@
var dataOptions = ["Steps", "Battery", "BattVolt", "VREF", "HRM", "Temp", "Humidity", "Wind", "Altitude", "CoreT"];
var speedOptions = ["kph", "mph"];
var color_options = ['Green','Orange','Cyan','Purple','Red','Blue','Yellow','White','Purple','Pink','Light Green','Dark Green'];
var bg_code = ['#00ff00','#FF9900','#0094FF','#FF00DC','#ff0000','#0000ff','#ffef00','#FFFFFF','#FF00FF','#6C00FF','#99FF00','#556B2F'];
var color_options = [
'Green', 'Orange', 'Cyan', 'Purple', 'Red', 'Blue', 'Yellow', 'White',
'Purple', 'Pink', 'Light Green', 'Brown', 'Turquoise', 'Magenta', 'Lime',
'Gold', 'Sky Blue', 'Rose', 'Lavender', 'Amber', 'Indigo', 'Teal',
'Crimson', 'Maroon', 'Firebrick', 'Dark Red', 'Aqua', 'Emerald', 'Royal Blue',
'Sunset Orange', 'Turquoise Blue', 'Hot Pink', 'Goldenrod', 'Deep Sky Blue'
];
var bg_code = [
'#00ff00', '#FF9900', '#0094FF', '#FF00DC', '#ff0000', '#0000ff', '#ffef00', '#FFFFFF',
'#FF00FF', '#6C00FF', '#99FF00', '#8B4513', '#40E0D0', '#FF00FF', '#00FF00', '#FFD700',
'#87CEEB', '#FF007F', '#E6E6FA', '#FFBF00', '#4B0082', '#008080', '#DC143C', '#800000',
'#B22222', '#8B0000', '#00FFFF', '#008000', '#4169E1', '#FF4500', '#40E0D0', '#FF69B4',
'#DAA520', '#00BFFF'
];
E.showMenu({
'': { 'title': 'LCARS Clock' },
'< Back': back,
@ -80,7 +93,7 @@
},
'Theme Color 1': {
value: 0 | bg_code.indexOf(settings.themeColor1BG),
min: 0, max: 11,
min: 0, max: 34,
format: v => color_options[v],
onchange: v => {
settings.themeColor1BG = bg_code[v];
@ -89,7 +102,7 @@
},
'Theme Color 2': {
value: 0 | bg_code.indexOf(settings.themeColor2BG),
min: 0, max: 11,
min: 0, max: 34,
format: v => color_options[v],
onchange: v => {
settings.themeColor2BG = bg_code[v];
@ -98,7 +111,7 @@
},
'Theme Color 3': {
value: 0 | bg_code.indexOf(settings.themeColor3BG),
min: 0, max: 11,
min: 0, max: 34,
format: v => color_options[v],
onchange: v => {
settings.themeColor3BG = bg_code[v];
@ -121,5 +134,13 @@
save();
},
},
'Random colors on open': {
value: settings.randomColors,
format: () => (settings.randomColors ? 'Yes' : 'No'),
onchange: () => {
settings.randomColors = !settings.randomColors;
save();
},
},
});
})

View File

@ -3,7 +3,7 @@
"name": "LCARS Clock",
"shortName":"LCARS",
"icon": "lcars.png",
"version":"0.29",
"version":"0.30",
"readme": "README.md",
"supports": ["BANGLEJS2"],
"description": "Library Computer Access Retrieval System (LCARS) clock.",

View File

@ -20,7 +20,7 @@ function readFile(input) {
const jCalData = ICAL.parse(icalText);
const comp = new ICAL.Component(jCalData);
const vtz = comp.getFirstSubcomponent('vtimezone');
const tz = new ICAL.Timezone(vtz);
const tz = vtz != null ? new ICAL.Timezone(vtz) : null;
// Fetch the VEVENT part
comp.getAllSubcomponents('vevent').forEach(vevent => {
@ -73,7 +73,9 @@ function getAlarmDefaults() {
}
function eventToAlarm(event, tz, offsetMs) {
if (tz != null) {
event.startDate.zone = tz;
}
const dateOrig = event.startDate.toJSDate();
const date = offsetMs ? new Date(dateOrig - offsetMs) : dateOrig;