mirror of
https://github.com/Telecominfraproject/wlan-ap.git
synced 2025-10-29 17:42:41 +00:00
Ensure that LEDs are configured to be ON before attempting to change their state.
Previously, if the LED was configured to be OFF, it would still enter a double-blink
state when the cloud connection was lost, and then switch to solid ON upon
reconnection—ignoring the configured OFF state.
This update changes that behavior:
- If LEDs are configured OFF, they will remain OFF even during cloud
disconnection (no double-blink).
- After temporary state changes (e.g., during factory reset), the LED will
return to its configured state (either OFF or ON).
Signed-off-by: Paul White <paul@shasta.cloud>
301 lines
7.2 KiB
Plaintext
Executable File
301 lines
7.2 KiB
Plaintext
Executable File
#!/usr/bin/ucode
|
|
|
|
'use strict';
|
|
|
|
import * as libubus from 'ubus';
|
|
import * as libuci from 'uci';
|
|
import * as uloop from 'uloop';
|
|
import * as nl80211 from 'nl80211';
|
|
import * as rtnl from 'rtnl';
|
|
import * as fs from 'fs';
|
|
import { ulog_open, ulog, ULOG_SYSLOG, ULOG_STDIO, LOG_DAEMON, LOG_INFO } from 'log';
|
|
|
|
ulog_open(ULOG_SYSLOG, LOG_DAEMON, "state");
|
|
|
|
uloop.init();
|
|
|
|
let ubus = libubus.connect();
|
|
let uci = libuci.cursor();
|
|
let config;
|
|
let offline_timer;
|
|
let current_state;
|
|
let online = false;
|
|
let leds_off = false;
|
|
|
|
function self_healing() {
|
|
let heal_wifi = false;
|
|
let health_stat = json(fs.readfile('/tmp/ucentral.health'));
|
|
let last_nw_restart_ts = int(fs.readfile('/tmp/ucentral.nw_restart_ts')) || 0;
|
|
let time_passed_since_nw_restart = time() - last_nw_restart_ts;
|
|
|
|
if (health_stat) {
|
|
if (health_stat.data.rrm_chanutil == false) {
|
|
// RRM with Channel utilization abnormal, restart rrmd
|
|
ulog(LOG_INFO, 'RRM with Channel utilization abnormal, restarting rrmd\n');
|
|
system('/etc/init.d/rrmd restart');
|
|
}
|
|
|
|
if (health_stat.sanity != 100) {
|
|
for (let iface in health_stat.data.interfaces) {
|
|
let iface_data = health_stat.data.interfaces[iface];
|
|
if (iface_data.ssids) {
|
|
// one of the VAPs have an issue: flag up!
|
|
heal_wifi = true;
|
|
ulog(LOG_INFO, 'Time passed since last network restart = %d seconds\n', time_passed_since_nw_restart);
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
// all VAPs are healthy, no need to heal anything
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (fs.stat('/tmp/rrm_timestamp')) {
|
|
let rrm_chan_switch_flag = int(fs.readfile('/tmp/rrm_chan_switch')) || 0;
|
|
let last_rrm_timestamp = int(fs.readfile('/tmp/rrm_timestamp'));
|
|
let time_passed_since_rrm = time() - last_rrm_timestamp;
|
|
|
|
if (rrm_chan_switch_flag == 1) {
|
|
// RRM chan switch in progress, do not restart network!
|
|
ulog(LOG_INFO, 'RRM channel switch in progress, cannot restart network \n');
|
|
heal_wifi = false;
|
|
}
|
|
|
|
if (time_passed_since_rrm < 180) {
|
|
// RRM in progress, do not restart network!
|
|
ulog(LOG_INFO, 'RRM with Channel utilization may still be in progress, cannot restart network \n');
|
|
heal_wifi = false;
|
|
}
|
|
}
|
|
|
|
// keep a gap of at least 5 minutes between network restarts
|
|
if (heal_wifi && time_passed_since_nw_restart > 300) {
|
|
ulog(LOG_INFO, 'Restarting network \n');
|
|
// update network restart timestamp
|
|
let f = fs.open("/tmp/ucentral.nw_restart_ts", "w");
|
|
if (f) {
|
|
f.write(time());
|
|
f.close();
|
|
}
|
|
|
|
// restart network
|
|
system('/etc/init.d/network restart');
|
|
}
|
|
}
|
|
|
|
let healthcheck;
|
|
healthcheck = {
|
|
run: function(delay) {
|
|
if (healthcheck.pid)
|
|
healthcheck.pid.delete();
|
|
ulog(LOG_INFO, 'start healthcheck in ' + delay / 1000 + ' seconds\n');
|
|
if (healthcheck.interval)
|
|
healthcheck.interval.set(delay);
|
|
else
|
|
healthcheck.interval = uloop.interval(delay, healthcheck.spawn);
|
|
},
|
|
|
|
complete: function() {
|
|
self_healing();
|
|
},
|
|
|
|
spawn: function() {
|
|
ulog(LOG_INFO, 'healthcheck execute\n');
|
|
healthcheck.pid = uloop.process('/usr/share/ucentral/health.uc', [], {}, healthcheck.complete);
|
|
},
|
|
};
|
|
|
|
let state;
|
|
state = {
|
|
run: function(delay) {
|
|
if (state.pid)
|
|
state.pid.delete();
|
|
ulog(LOG_INFO, 'start state in ' + delay / 1000 + ' seconds\n');
|
|
if (state.interval)
|
|
state.interval.set(delay);
|
|
else
|
|
state.interval = uloop.interval(delay, state.spawn);
|
|
},
|
|
|
|
complete: function() {
|
|
},
|
|
|
|
spawn: function() {
|
|
if (!online) {
|
|
ulog(LOG_INFO, 'offline - skipping state execution\n');
|
|
return;
|
|
}
|
|
ulog(LOG_INFO, 'state execute\n');
|
|
state.pid = uloop.process('/usr/share/ucentral/state.uc', [], {}, state.complete);
|
|
},
|
|
};
|
|
|
|
function offline_handler() {
|
|
let status = ubus.call('ucentral', 'status');
|
|
if (status?.connected)
|
|
return;
|
|
ulog(LOG_INFO, 'going offline\n');
|
|
ubus.call('network.interface.admin_ui', 'up');
|
|
}
|
|
|
|
function online_handler() {
|
|
ulog(LOG_INFO, 'going online\n');
|
|
ubus.call('network.interface.admin_ui', 'down');
|
|
if (offline_timer) {
|
|
offline_timer.cancel();
|
|
}
|
|
}
|
|
|
|
function config_load() {
|
|
ulog(LOG_INFO, 'loading config\n');
|
|
|
|
uci.load('system');
|
|
let led_off_cfg = uci.get("system", "@system[0]", "leds_off");
|
|
if (led_off_cfg == 1) {
|
|
leds_off = true;
|
|
}
|
|
|
|
uci.load('state');
|
|
config = uci.get_all('state');
|
|
|
|
if (healthcheck?.interval)
|
|
healthcheck.interval.cancel();
|
|
if (config?.health?.interval)
|
|
healthcheck.run(config?.health?.interval * 1000);
|
|
|
|
if (state?.interval)
|
|
state.interval.cancel();
|
|
if (config?.stats?.interval)
|
|
state.run(config?.stats?.interval * 1000);
|
|
|
|
let status = ubus.call('ucentral', 'status');
|
|
if (status?.connected) {
|
|
online_handler();
|
|
online = true;
|
|
} else if (config.ui.offline_trigger)
|
|
offline_timer = uloop.timer(config.ui.offline_trigger * 1000, offline_handler);
|
|
}
|
|
|
|
function led_write(led, property, value) {
|
|
let path = '/sys/class/leds/' + led + '/' + property;
|
|
let current = trim(fs.readfile(path));
|
|
if (current == value)
|
|
return;
|
|
let file = fs.open(path, 'w');
|
|
if (!file)
|
|
return;
|
|
file.write(value);
|
|
file.close();
|
|
}
|
|
|
|
function led_find(alias) {
|
|
let path = fs.readfile('/proc/device-tree/aliases/' + alias);
|
|
if (!path)
|
|
return;
|
|
return trim(fs.readfile('/proc/device-tree/' + trim(path) + '/label'));
|
|
}
|
|
|
|
function factory_reset_timeout() {
|
|
let led = led_find('led-running');
|
|
if (led)
|
|
led_write(led, 'trigger', leds-off ? 'none' : 'default-on');
|
|
}
|
|
|
|
let blink_timer;
|
|
function blink_timeout() {
|
|
if (current_state == 'blink') {
|
|
current_state = 'online';
|
|
}
|
|
system('/etc/init.d/led turnon');
|
|
if (!blink_timer)
|
|
return;
|
|
blink_timer.cancel();
|
|
blink_timer = null;
|
|
}
|
|
|
|
let state_handler = {
|
|
offline: function() {
|
|
online = false;
|
|
let led = led_find('led-running');
|
|
if (!leds_off && led)
|
|
led_write(led, 'trigger', 'heartbeat');
|
|
if (config.ui.offline_trigger) {
|
|
if (offline_timer)
|
|
offline_timer.cancel();
|
|
offline_timer = uloop.timer(config.ui.offline_trigger * 1000, offline_handler);
|
|
}
|
|
return 0;
|
|
},
|
|
|
|
online: function() {
|
|
online = true;
|
|
let led = led_find('led-running');
|
|
if (!leds_off && led)
|
|
led_write(led, 'trigger', 'default-on');
|
|
online_handler();
|
|
return 0;
|
|
},
|
|
|
|
'factory-reset': function() {
|
|
let led = led_find('led-running');
|
|
if (!led)
|
|
return ubus.STATUS_INVALID_ARGUMENT;
|
|
led_write(led, 'trigger', 'timer');
|
|
led_write(led, 'delay_on', '100');
|
|
led_write(led, 'delay_off', '100');
|
|
uloop.timer(6000, factory_reset_timeout);
|
|
return 0;
|
|
},
|
|
|
|
blink: function(args) {
|
|
if (!args.duration) {
|
|
blink_timeout();
|
|
return 0;
|
|
}
|
|
system('/etc/init.d/led blink');
|
|
if (args.duration != 0xffff)
|
|
blink_timer = uloop.timer((args.duration || 10) * 1000, blink_timeout);
|
|
return 0;
|
|
},
|
|
};
|
|
|
|
let ubus_methods = {
|
|
set: {
|
|
call: function(req) {
|
|
ulog(LOG_INFO, 'state %s -> %s\n', current_state, req.args.state);
|
|
if (current_state == req.args.state && req.args.state != 'blink')
|
|
return;
|
|
if (!state_handler[req.args.state])
|
|
return ubus.STATUS_INVALID_ARGUMENT;
|
|
if (current_state != req.args.state)
|
|
ulog(LOG_INFO, 'state %s -> %s\n', current_state, req.args.state);
|
|
current_state = req.args.state;
|
|
blink_timeout();
|
|
ulog(LOG_INFO, 'set state -> ' + req.args.state + '\n');
|
|
|
|
return state_handler[req.args.state](req.args);
|
|
},
|
|
args: {
|
|
state:'',
|
|
duration: 0
|
|
}
|
|
},
|
|
|
|
reload: {
|
|
call: function(req) {
|
|
config_load();
|
|
return 0;
|
|
},
|
|
args: {
|
|
|
|
}
|
|
},
|
|
};
|
|
|
|
ubus.publish('state', ubus_methods);
|
|
config_load();
|
|
|
|
uloop.run();
|
|
uloop.done();
|