forked from FOSS/BangleApps
Merge branch 'espruino:master' into master
commit
0375147ee5
|
@ -0,0 +1 @@
|
|||
0.01: attempt to import
|
|
@ -0,0 +1,8 @@
|
|||
# Bad Apple demo 
|
||||
|
||||
"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.
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwgIifiAFWj//Aod///gAgMH///+AFBn4FB/AQDAoYEB//8gEBAokDAoX+ApguCAAIFqGoYFNLIZHBApZxFAoyDCAoqJCSoqsBUIcPAoKtF4AFBJAS/DHIQAeA="))
|
Binary file not shown.
After Width: | Height: | Size: 9.4 KiB |
|
@ -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);
|
Binary file not shown.
Binary file not shown.
|
@ -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}
|
||||
]
|
||||
}
|
|
@ -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)
|
|
@ -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.*
|
|
@ -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")
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
},
|
||||
},
|
||||
});
|
||||
})
|
||||
|
|
|
@ -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.",
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in New Issue