dlite/fs/init.js

408 lines
13 KiB
JavaScript

load('api_config.js');
load('api_gpio.js');
load('api_mqtt.js');
load('api_net.js');
load('api_sys.js');
load('api_rpc.js');
load('api_timer.js');
load('api_file.js');
load('api_dlite.js');
// Helper C function get_led_gpio_pin() in src/main.c returns built-in LED GPIO
// let button = Cfg.get('pins.button');
let topic = '/devices/' + Cfg.get('device.id') + '/events';
// let onBoardLed = ffi('int get_led_gpio_pin(void)')();
// print('LED GPIO:', onBoardLed, 'button GPIO:', button);
let getInfo = function() {
return JSON.stringify({
total_ram: Sys.total_ram(),
free_ram: Sys.free_ram(),
uptime: Sys.uptime()
});
};
// Blink built-in LED every second
/*
GPIO.set_mode(onBoardLed, GPIO.MODE_OUTPUT);
Timer.set(5000, true, function() {
let value = GPIO.toggle(onBoardLed);
print(value ? 'Tick' : 'Tock', 'uptime:', Sys.uptime(), getInfo());
}, null);
*/
// Publish to MQTT topic on a button press. Button is wired to GPIO pin 0
/*
GPIO.set_button_handler(button, GPIO.PULL_UP, GPIO.INT_EDGE_NEG, 200, function() {
let message = getInfo();
let ok = MQTT.pub(topic, message, 1);
print('Published:', ok, topic, '->', message);
}, null);
*/
// Monitor network connectivity.
Net.setStatusEventHandler(function(ev, arg) {
let evs = '???';
if (ev === Net.STATUS_DISCONNECTED) {
evs = 'DISCONNECTED';
} else if (ev === Net.STATUS_CONNECTING) {
evs = 'CONNECTING';
} else if (ev === Net.STATUS_CONNECTED) {
evs = 'CONNECTED';
} else if (ev === Net.STATUS_GOT_IP) {
evs = 'GOT_IP';
}
print('==> NET:', ev, evs);
}, null);
let pin = Cfg.get('led.pin');
let configNumLeds = Cfg.get('led.count');
let colorFile = Cfg.get('led.colorFile');
let animationFile = Cfg.get('led.animationFile');
let lampsFile = Cfg.get('led.lampsFile');
let scheduleFile = Cfg.get('led.scheduleFile');
let dynamicMode = Cfg.get('led.mode');
let useDefaults = Cfg.get('led.useDefaults');
let tickDuration = Cfg.get('led.tickDuration');
let numberOfLeds = configNumLeds, numberOfLedDefs = 0; // from config files, count led definition entries
/* Create test pattern */
function showLedTestPattern() {
let i;
let switchMod;
for (i=0; i<configNumLeds; ++i) {
switchMod = i - 6 * Math.floor(i/6);
if (switchMod === 0) NeoPixel_set(i, 10, 10, 10);
if (switchMod === 1) NeoPixel_set(i, 10, 10, 10);
if (switchMod === 2) NeoPixel_set(i, 10, 100, 0);
if (switchMod === 3) NeoPixel_set(i, 0, 10, 0);
if (switchMod === 4) NeoPixel_set(i, 0, 10, 10);
if (switchMod === 5) NeoPixel_set(i, 0, 0, 10);
if (switchMod < 0 || switchMod > 5) print("WRONG -- should never reach this in switch statement!");
}
NeoPixel_show();
}
function allLedOn() {
let i;
for (i=0; i<configNumLeds; ++i) {
NeoPixel_set(i, 10, 10, 10);
}
NeoPixel_show();
}
function allLedOff() {
NeoPixel_clear();
NeoPixel_show();
}
function validateColor(color) {
if (color.name.length === 0) {
print('ERROR: Color name missing!');
return false;
}
if ((color.color.length !== 4 && color.color.length !== 7) || color.color[0] !== '#') {
print('ERROR: Color', color.name, 'has an invalid color definition.');
print(' Is:', color.color, ' but should #rgb where r/g/b is a 1-digit hex number in the range of 0..9a..f or #RGB where R/G/B is a 2-digit hex number');
return false;
}
for (let i=0; i<color.color.length; ++i) {
if (color.color[i] < '0' || (color.color[i] > '9' && (color.color[i] < 'a' || color.color[i] > 'f'))) {
print('ERROR: Color', color.name, 'has an invalid color definition.');
print(' Is:', color.color, ' but should #rgb where r/g/b is a 1-digit hex number in the range of 0..9a..f or #RGB where R/G/B is a 2-digit hex number');
return false;
}
}
return true;
}
function hexToDec(digit) {
if (digit >= 'a') return digit - 'a';
else if (digit >= 'A') return digit - 'A';
else return digit - '0';
}
function getRedOfColorString(color) {
if (color.length === 4) {
return hexToDec(color.color[1]);
} else {
return hexToDec(color.color[1]) * 16 + hexToDec(color.color[2]);
}
}
function getGreenOfColorString(color) {
if (color.length === 4) {
return hexToDec(color.color[2]);
} else {
return hexToDec(color.color[3]) * 16 + hexToDec(color.color[4]);
}
}
function getBlueOfColorString(color) {
if (color.length === 4) {
return hexToDec(color.color[3]);
} else {
return hexToDec(color.color[5]) * 16 + hexToDec(color.color[6]);
}
}
function loadColorDefs() {
// Load Color definitions
let json = File.read(colorFile);
let colors = [];
print('colorJson =', json);
if (json === '') {
print('ERROR: Color definition file does not exist!');
} else {
colors = JSON.parse(json);
}
for (i=0; i<colors.length; ++i) {
print('- addColor', colors[i].name, colors[i].color);
colors[i].red = getRedOfColorString(colors[i]);
colors[i].green = getGreenOfColorString(colors[i]);
colors[i].blue = getBlueOfColorString(colors[i]);
addColor(colors[i].name, colors[i].red, colors[i].green, colors[i].blue);
}
json = null;
// colors = null; // NO! Do not do this, as the strings are still referenced!
}
function loadLedDefs() {
// Load LED Definitions
let json = File.read(lampsFile);
let ledDef = [];
print('lampsFile =', json);
if (json === '') {
print('ERROR: LED definition file does not exist!');
} else {
ledDef = JSON.parse(json);
}
numberOfLedDefs = 0;
for (i=0; i<ledDef.length; ++i) {
++numberOfLedDefs;
print('- addLedDefinition', ledDef[i].level, ledDef[i].room, ledDef[i].id, ledDef[i].onColor);
LEDDefinition_addByName(ledDef[i].level, ledDef[i].room, ledDef[i].id, ledDef[i].onColor);
// printColor(ledDef[i].onColor);
}
if (numberOfLedDefs > configNumLeds) {
print('WARNING: LED definition file contains more LEDs (', numberOfLedDefs, ') than configured in system-config (', configNumLeds, ')');
}
json = null;
// ledDef = null; // NO! Do not do this, as the strings are still referenced!
}
function loadAnimDefs() {
// Load Animation Definitions
let json = File.read(animationFile);
let animation = [];
print('animationFile =', json);
if (json === '') {
print('ERROR: Animation definition file does not exist!');
} else {
animation = JSON.parse(json);
}
for (i=0; i<animation.length; ++i) {
print('- addAnimationStep', animation[i].led, animation[i].mode, animation[i].ticks);
addAnimationStep(animation[i].led, animation[i].mode, animation[i].ticks);
}
json = null;
}
function loadScheduleDefs() {
// Load Animation Definitions
let json = File.read(scheduleFile);
let scheduleDef = [];
print('scheduleFile =', json);
if (json === '') {
print('ERROR: Schedule definition file does not exist!');
} else {
scheduleDef = JSON.parse(json);
}
LEDScheduler_init(scheduleDef.schedule.length, numberOfLeds, pin);
LEDScheduler_setWatch(2, 0, 1); // time 02:00, clock speed = 1 real time second per model minute
let ledMapping = scheduleDef.lampmapping;
let mapLed = function (name) {
for (let i=0; i<ledMapping.length; ++i) {
if (ledMapping[i] === name) return i;
}
print("**** ERROR in schedule definition file: no lamp mapping found for lamp", name);
return 0;
};
for (i=0; i<scheduleDef.schedule.length; ++i) {
print('- addScheduleItem', scheduleDef.schedule[i].time.h, ":", scheduleDef.schedule[i].time.m, "/",
scheduleDef.schedule[i].lamp, "=", mapLed(scheduleDef.schedule[i].lamp), "/",
scheduleDef.schedule[i].mode, "/",
scheduleDef.schedule[i].color);
LEDScheduler_addItem(scheduleDef.schedule[i].time.h, scheduleDef.schedule[i].time.m,
mapLed(scheduleDef.schedule[i].lamp),
scheduleDef.schedule[i].mode,
scheduleDef.schedule[i].color);
}
json = null;
}
function initialize() {
print('***** Start initialization', getInfo());
let i;
if (useDefaults) {
print('Using default color definition');
numberOfLeds = configNumLeds;
startLEDStateEngine();
} else {
loadColorDefs();
if (dynamicMode === "animation") {
loadLedDefs();
loadAnimDefs();
// Initialize LED State Engine
LEDStateEngine_init(pin, numberOfLeds);
print('LED', 'Color', 'R', 'G', 'B', 'Tick', 'Level', 'Room', 'Id');
print('---', '-----', '---', '---', '-----', '---', '-----', '----', '--');
for (i=0; i<numberOfLeds; ++i) {
// print(i, LEDState_getRed(i), LEDState_getGreen(i), LEDState_getBlue(i), LEDState_getCurrentTick(i), LEDDefinition_getLevel(i), LEDDefinition_getRoom(i), LEDDefinition_getId(i))
print(i, LEDState_getColorName(i),
LEDDefinition_getOnColorRed(i),
LEDDefinition_getOnColorGreen(i),
LEDDefinition_getOnColorBlue(i),
LEDState_getCurrentTick(i),
LEDDefinition_getLevel(i),
LEDDefinition_getRoom(i),
LEDDefinition_getId(i));
}
} else if (dynamicMode === "schedule") {
loadScheduleDefs();
} else {
print("**** ERROR: Unknown dynamic mode", dynamicMode);
return;
}
}
allLedOff();
print('***** End of initialization', getInfo());
print('_______________________________________________________');
print('LedPin:', pin);
print('NumLEDs:', numberOfLeds);
print('Ticks:', getTicks());
print('Tick duration:', tickDuration, 'ms');
print('Brightness:', NeoPixel_getBrightness(), '%');
}
Timer.set(300, false, function() {
initialize();
allLedOn();
print('***** Starting timer to delay test pattern generation');
allLedOff();
print('***** LED test pattern', getInfo());
showLedTestPattern();
if (dynamicMode === "animation") {
print('***** Start LED state engine and LED-update timer', getInfo());
LEDStateEngine_start();
} else if (dynamicMode === "schedule") {
print('***** Start schedule based animation', getInfo());
NeoPixel_clear();
LEDScheduler_run();
}
Timer.set(30000, true, function() {
print("Timer: minTickTime =", LEDStateEngine_getMinTickTime(),
"ms, maxTickTime =", LEDStateEngine_getMaxTickTime(), "ms");
}, null);
}, null);
/* ------ RPC Handlers ------- */
print('**** Adding RPC handlers');
print(' led.setBrightness');
RPC.addHandler('led.setBrightness', function(args) {
// print(args);
if (args !== undefined && args.level !== undefined) {
if (args.level > 0 && args.level <= 100) {
NeoPixel_setBrightness(args.level);
return { result: 'ok' };
} else {
return { error: 'Brightness level must be in the range 1..100' };
}
} else {
return { error: 'level is required having a value in the range 1..100' };
}
}, "{level: %d}");
print(' led.getBrightness');
RPC.addHandler('led.getBrightness', function(args) {
let brightness = NeoPixel_getBrightness();
return { result: 'ok', brightness: brightness };
}, null);
print(' led.pause');
RPC.addHandler('led.pause', function(args) {
LEDStateEngine_pause();
return { result: 'ok' };
}, null);
print(' led.run');
RPC.addHandler('led.run', function(args) {
LEDStateEngine_start();
return { result: 'ok' };
}, null);
print(' led.getColors');
RPC.addHandler('led.getColors', function(args) {
let colors = JSON.parse(File.read(colorFile));
return { result: 'ok', colors: colors };
}, null);
print(' led.getLamps');
RPC.addHandler('led.getLamps', function(args) {
let lamps = JSON.parse(File.read(lampsFile));
return { result: 'ok', lamps: lamps };
}, null);
print(' led.getAnimations');
RPC.addHandler('led.getAnimations', function(args) {
let anims = JSON.parse(File.read(animationFile));
return { result: 'ok', animations: anims };
}, null);
function saveFile(filename, content) {
let backupFilename = 'bak-' + filename;
File.remove(backupFilename);
File.rename(filename, backupFilename);
File.write(JSON.stringify(content), filename);
let backupFilename = null;
}
function recoverFile(filename) {
let newerFilename = 'new-' + filename;
let backupFilename = 'bak-' + filename;
let bakFileContent = File.read(backupFilename);
if (bakFileContent === '') {
Log.error('recoverFile(' + filename + '): Backup file ' + backupFilename + ' does not exist or is empty');
return;
}
File.remove(newerFilename);
File.rename(filename, newerFilename);
File.rename(backupFilename, filename);
}
print(' led.putColors');
RPC.addHandler('led.putColors', function(args) {
if (args !== undefined && args.colors !== undefined && args.colors[0] !== undefined && args.colors[0].name !== undefined && args.colors[0].color !== undefined) {
saveFile(colorFile, args.colors);
return { result: 'ok' };
} else {
return { error: 'colors: {name, color} is required' };
}
}, null);
print(' led.putLamps');
RPC.addHandler('led.putLamps', function(args) {
if (args !== undefined && args.lamps !== undefined) {
saveFile(lampsFile, args.lamps);
return { result: 'ok' };
} else {
return { error: 'lamps: {level, room, id, onColor} is required' };
}
}, null);
print(' led.putAnimations');
RPC.addHandler('led.putAnimations', function(args) {
if (args !== undefined && args.animations !== undefined) {
saveFile(animationFile, args.animations);
return { result: 'ok' };
} else {
return { error: 'animations: {led, mode, ticks} is required' };
}
}, null);
print('***** End of init.js, sub tasks still running', getInfo());