mirror of
https://github.com/rstrouse/ESPSomfy-RTS.git
synced 2025-12-12 18:42:10 +01:00
423 lines
20 KiB
HTML
423 lines
20 KiB
HTML
<!DOCTYPE html>
|
|
|
|
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
|
|
<head>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<meta charset="UTF-8">
|
|
<title>Configure CC1101</title>
|
|
<link rel="stylesheet" href="main.css" type="text/css" />
|
|
<link rel="stylesheet" href="icons.css" type="text/css" />
|
|
<style type="text/css">
|
|
body {
|
|
margin-top: 0px;
|
|
}
|
|
|
|
input[data-type="int8_t"],
|
|
input[data-type="uint8_t"] {
|
|
width: 47px;
|
|
text-align: right;
|
|
}
|
|
|
|
input[data-type="uint16_t"] {
|
|
width: 77px;
|
|
text-align: right;
|
|
}
|
|
|
|
input[data-type="float"] {
|
|
width: 77px;
|
|
text-align: right;
|
|
}
|
|
</style>
|
|
<script type="text/javascript">
|
|
let runRepair = false;
|
|
function runRemoteRepair() {
|
|
let obj = { address: parseInt(document.getElementById('fldRepairAddress').value, 10), command: 'Down', rcode: parseInt(document.getElementById('fldRepairRCode').value, 10), repeats: 1 };
|
|
if (runRepair) {
|
|
// Call the service.
|
|
console.log(obj);
|
|
putJSON('/sendRemoteCommand', obj, (err, ret) => {
|
|
console.log(ret);
|
|
document.getElementById('fldRepairRCode').value = obj.rcode + 1;
|
|
setTimeout(() => { runRemoteRepair() }, 1000);
|
|
});
|
|
}
|
|
}
|
|
function stopRemoteRepair() {
|
|
runRepair = false;
|
|
}
|
|
|
|
let baseUrl = ''; //'http://192.168.1.204'; This is the current server.
|
|
|
|
Number.prototype.round = function (dec) { return Number(Math.round(this + 'e' + dec) + 'e-' + dec); };
|
|
Number.prototype.fmt = function (format, empty) {
|
|
if (isNaN(this)) return empty || '';
|
|
if (typeof format === 'undefined') return this.toString();
|
|
let isNegative = this < 0;
|
|
let tok = ['#', '0'];
|
|
let pfx = '', sfx = '', fmt = format.replace(/[^#\.0\,]/g, '');
|
|
let dec = fmt.lastIndexOf('.') > 0 ? fmt.length - (fmt.lastIndexOf('.') + 1) : 0,
|
|
fw = '', fd = '', vw = '', vd = '', rw = '', rd = '';
|
|
let val = String(Math.abs(this).round(dec));
|
|
let ret = '', commaChar = ',', decChar = '.';
|
|
for (var i = 0; i < format.length; i++) {
|
|
let c = format.charAt(i);
|
|
if (c === '#' || c === '0' || c === '.' || c === ',')
|
|
break;
|
|
pfx += c;
|
|
}
|
|
for (let i = format.length - 1; i >= 0; i--) {
|
|
let c = format.charAt(i);
|
|
if (c === '#' || c === '0' || c === '.' || c === ',')
|
|
break;
|
|
sfx = c + sfx;
|
|
}
|
|
if (dec > 0) {
|
|
let dp = val.lastIndexOf('.');
|
|
if (dp === -1) {
|
|
val += '.'; dp = 0;
|
|
}
|
|
else
|
|
dp = val.length - (dp + 1);
|
|
while (dp < dec) {
|
|
val += '0';
|
|
dp++;
|
|
}
|
|
fw = fmt.substring(0, fmt.lastIndexOf('.'));
|
|
fd = fmt.substring(fmt.lastIndexOf('.') + 1);
|
|
vw = val.substring(0, val.lastIndexOf('.'));
|
|
vd = val.substring(val.lastIndexOf('.') + 1);
|
|
let ds = val.substring(val.lastIndexOf('.'), val.length);
|
|
for (let i = 0; i < fd.length; i++) {
|
|
if (fd.charAt(i) === '#' && vd.charAt(i) !== '0') {
|
|
rd += vd.charAt(i);
|
|
continue;
|
|
} else if (fd.charAt(i) === '#' && vd.charAt(i) === '0') {
|
|
var np = vd.substring(i);
|
|
if (np.match('[1-9]')) {
|
|
rd += vd.charAt(i);
|
|
continue;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
else if (fd.charAt(i) === '0' || fd.charAt(i) === '#')
|
|
rd += vd.charAt(i);
|
|
}
|
|
if (rd.length > 0) rd = decChar + rd;
|
|
}
|
|
else {
|
|
fw = fmt;
|
|
vw = val;
|
|
}
|
|
var cg = fw.lastIndexOf(',') >= 0 ? fw.length - fw.lastIndexOf(',') - 1 : 0;
|
|
var nw = Math.abs(Math.floor(this.round(dec)));
|
|
if (!(nw === 0 && fw.substr(fw.length - 1) === '#') || fw.substr(fw.length - 1) === '0') {
|
|
var gc = 0;
|
|
for (let i = vw.length - 1; i >= 0; i--) {
|
|
rw = vw.charAt(i) + rw;
|
|
gc++;
|
|
if (gc === cg && i !== 0) {
|
|
rw = commaChar + rw;
|
|
gc = 0;
|
|
}
|
|
}
|
|
if (fw.length > rw.length) {
|
|
var pstart = fw.indexOf('0');
|
|
if (pstart > 0) {
|
|
var plen = fw.length - pstart;
|
|
var pos = fw.length - rw.length - 1;
|
|
while (rw.length < plen) {
|
|
let pc = fw.charAt(pos);
|
|
if (pc === ',') pc = commaChar;
|
|
rw = pc + rw;
|
|
pos--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (isNegative) rw = '-' + rw;
|
|
if (rd.length === 0 && rw.length === 0) return '';
|
|
//console.log(pfx + rw + rd + sfx);
|
|
return pfx + rw + rd + sfx;
|
|
};
|
|
let fields = [
|
|
{ t: 'bool', i: 'printBuffer', d: 'Print the bytes received to the serial console', def: false },
|
|
{ t: 'bool', i: 'internalCCMode', d: 'Use internal transmission mode FIFO buffers.', def:true },
|
|
{ t: 'uint8_t', i: 'modulationMode', d: 'Modulation mode', o: [{ v: 0, n: '2-FSK' }, { v: 1, n: 'GFSK' }, { v: 2, n: 'ASK/OOK' }, { v: 3, n: '4-FSK' }, { v: 4, n: 'MSK' }] },
|
|
{ t: 'float', i: 'frequency', d: 'Basic frequency', def:433.24 },
|
|
{ t: 'float', i: 'deviation', d: 'Set the Frequency deviation in kHz. Value from 1.58 to 380.85. Default is 47.60 kHz.', def:47.6 },
|
|
{ t: 'uint8_t', i: 'channel', d: 'The channel number from 0 to 255', def:0 },
|
|
{ t: 'float', i: 'channelSpacing', d: 'Channel spacing in multiplied by the channel number and added to the base frequency in kHz. 25.39 to 405.45. Default 199.95', def:199.95 },
|
|
{ t: 'float', i: 'rxBandwidth', d: 'Receive bandwidth in kHz. Value from 58.03 to 812.50. Default is 812.50kHz', def:812.5 },
|
|
{ t: 'float', i: 'dataRate', d: 'The data rate in kBaud. 0.02 to 1621.83 Default is 99.97.', def:99.97 },
|
|
{ t: 'int8_t', i: 'txPower', d: 'Transmission power {-30, -20, -15, -10, -6, 0, 5, 7, 10, 11, 12}. Default is 12.', def:12 },
|
|
{
|
|
t: 'uint8_t', i: 'syncMode', d: 'Sync Mode', def:0, o: [
|
|
{ v: 0, n: 'No preamble/sync' },
|
|
{ v: 1, n: '16 sync word bits detected' },
|
|
{ v: 2, n: '16/16 sync words bits detected.' },
|
|
{ v: 3, n: '30/32 sync word bits detected,' },
|
|
{ v: 4, n: 'No preamble/sync carrier above threshold' },
|
|
{ v: 5, n: '15/16 + carrier above threshold.' },
|
|
{ v: 6, n: '16/16 + carrier-sense above threshold' },
|
|
{ v: 7, n: '0/32 + carrier-sense above threshold' }]
|
|
},
|
|
{ t: 'uint16_t', i: 'syncWordHigh', d: 'The High sync word used for the sync mode.', def:211 },
|
|
{ t: 'uint16_t', i: 'syncWordLow', d: 'The Low sync word used for the sync mode.', def:145 },
|
|
{
|
|
t: 'uint8_t', i: 'addrCheckMode', d: 'Address filter check mode', def:0, o: [
|
|
{ v: 0, n: 'No address filtration' },
|
|
{ v: 1, n: 'Check address without broadcast.' },
|
|
{ v: 2, n: 'Address check with 0 as broadcast.' },
|
|
{ v: 3, n: 'Address check with 0 or 255 as broadcast.' }]
|
|
},
|
|
{ t: 'uint8_t', i: 'checkAddr', d: 'Packet filter address depending on addrCheck settings.', def:0 },
|
|
{ t: 'bool', i: 'dataWhitening', d: 'Indicates whether data whitening should be applied.', def:false },
|
|
{
|
|
t: 'uint8_t', i: 'pktFormat', d: 'Packet formatting', def:0, o: [
|
|
{ v: 0, n: 'Use FIFO buffers for RX and TX' },
|
|
{ v: 1, n: 'Synchronous serial mode. RX on GDO0 and TX on either GDOx pins.' },
|
|
{ v: 2, n: 'Random TX mode. Send data using PN9 generator.' },
|
|
{ v: 3, n: 'Asynchronous serial mode. RX on GDO0 and TX on either GDOx pins.' }]
|
|
},
|
|
{
|
|
t: 'uint8_t', i: 'pktLengthMode', d: 'Type of packets', def:1, o: [
|
|
{ v: 0, n: 'Fixed packet length' },
|
|
{ v: 1, n: 'Variable packet length' },
|
|
{ v: 2, n: 'Infinite packet length' },
|
|
{ v: 3, n: 'Reserved' }]
|
|
},
|
|
{ t: 'uint8_t', i: 'pktLength', d: 'Packet length', def:0 },
|
|
{ t: 'bool', i: 'useCRC', d: 'Indicates whether CRC is to be used.', def:true },
|
|
{ t: 'bool', i: 'autoFlushCRC', d: 'Automatically flush RX FIFO when CRC fails. If more than one packet is in the buffer it too will be flushed.', def:false },
|
|
{ t: 'bool', i: 'disableDCFilter', d: 'Digital blocking filter for demodulator. Only for data rates <= 250k.', def:false },
|
|
{ t: 'bool', i: 'enableManchester', d: 'Enable/disable Manchester encoding.', def:false },
|
|
{ t: 'bool', i: 'enableFEC', d: 'Enable/disable forward error correction.', def:false },
|
|
{
|
|
t: 'uint8_t', i: 'minPreambleBytes', d: 'The minimum number of preamble bytes to be transmitted.', def:0, o: [
|
|
{ v: 0, n: '2bytes' },
|
|
{ v: 1, n: '3bytes' },
|
|
{ v: 2, n: '4bytes' },
|
|
{ v: 3, n: '6bytes' },
|
|
{ v: 4, n: '8bytes' },
|
|
{ v: 5, n: '12bytes' },
|
|
{ v: 6, n: '16bytes' },
|
|
{ v: 7, n: '24bytes' }]
|
|
},
|
|
{ t: 'uint8_t', i: 'pqtThreshold', d: 'Preamble quality estimator threshold.', def:0 },
|
|
{ t: 'bool', i: 'appendStatus', d: 'Appends the RSSI and LQI values to the TX packed as well as the CRC.', def: false }
|
|
];
|
|
function resetDefaults() {
|
|
for (let i = 0; i < fields.length; i++) {
|
|
let f = fields[i];
|
|
if (typeof f.def === 'undefined') continue;
|
|
let el = document.getElementById(`id_${f.i}`);
|
|
switch (f.t) {
|
|
case 'bool':
|
|
el.checked = f.def;
|
|
break;
|
|
default:
|
|
el.value = f.def;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
function loadConfig() {
|
|
let overlay = waitMessage(document.getElementById('divFields'));
|
|
getJSON('/getRadio', (err, radio) => {
|
|
overlay.remove();
|
|
if (err) {
|
|
console.log(err);
|
|
serviceError(document.getElementById('divFields'), err);
|
|
}
|
|
else {
|
|
console.log(radio);
|
|
let cfg = radio.config;
|
|
// Bind up all the fields.
|
|
let elems = document.querySelectorAll('[data-bind]');
|
|
for (let i = 0; i < elems.length; i++) {
|
|
let el = elems[i];
|
|
switch (el.dataset.type) {
|
|
case 'bool':
|
|
el.checked = cfg[el.dataset.bind];
|
|
break;
|
|
case 'float':
|
|
el.value = cfg[el.dataset.bind].fmt("#.##");
|
|
break;
|
|
default:
|
|
el.value = cfg[el.dataset.bind];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
function createCheckbox(f) { return `<input type="checkbox" id="id_${f.i}" name="${f.i}" data-type="${f.t}" data-bind="${f.bind || f.i}"></input><label for="${f.i}>${f.l || f.i}</label>`; }
|
|
function createNumberField(f) { return `<input type="number" id="id_${f.i}" name="${f.i}" data-type="${f.t}" data-bind="${f.bind || f.i}"></input > <label for="${f.i}>${f.l || f.i}</label>`; }
|
|
function createDropdown(f) {
|
|
let dd = `<select id="id_${f.i}" name="${f.i}" data-type="${f.t}"" data-bind="${f.bind || f.i}">`;
|
|
for (let i = 0; i < f.o.length; i++) {
|
|
let o = f.o[i];
|
|
dd += `<option value="${o.v}">${o.n}</option>`
|
|
}
|
|
dd += '</select>';
|
|
dd += `<label for="${f.i}>${f.l || f.i}</label>`;
|
|
return dd;
|
|
}
|
|
function createFields() {
|
|
let shtml = '<div id="divAttrs">';
|
|
for (let i = 0; i < fields.length; i++) {
|
|
let f = fields[i];
|
|
shtml += '<div class="cfg-attr"><div class="cfg-attr-field">'
|
|
switch (f.t) {
|
|
case 'bool':
|
|
shtml += createCheckbox(f);
|
|
break;
|
|
case 'uint8_t':
|
|
case 'int8_t':
|
|
case 'uint16_t':
|
|
case 'float':
|
|
case 'int8_t':
|
|
shtml += typeof f.o === 'undefined' ? createNumberField(f) : createDropdown(f);
|
|
break;
|
|
}
|
|
shtml += `</div><div class="cfg-attr-desc">${f.d}</div>`
|
|
shtml += '</div>';
|
|
}
|
|
shtml += '</div>';
|
|
document.getElementById('divFields').innerHTML = shtml;
|
|
}
|
|
function serviceError(el, err) {
|
|
let msg = '';
|
|
if (typeof err === 'string' && err.startsWith('{')) {
|
|
let e = JSON.parse(err);
|
|
if (typeof e !== 'undefined' && typeof e.desc === 'string') msg = e.desc;
|
|
else msg = err;
|
|
}
|
|
else if (typeof err === 'string') {
|
|
msg = err;
|
|
}
|
|
else if (typeof err === 'number') {
|
|
switch (err) {
|
|
case 404:
|
|
msg = `404: Service not found`;
|
|
break;
|
|
default:
|
|
msg = `${err}: Network HTTP Error`;
|
|
break;
|
|
}
|
|
}
|
|
else if (typeof err !== 'undefined') {
|
|
console.log(err);
|
|
if (typeof err.desc === 'string') msg = typeof err.desc !== 'undefined' ? err.desc : err.message;
|
|
}
|
|
return errorMessage(el, msg);
|
|
}
|
|
function errorMessage(el, msg) {
|
|
let div = document.createElement('div');
|
|
div.innerHTML = '<div class="innerError">' + msg + '</div><button type="button" onclick="clearErrors();">Close</button></div>';
|
|
div.classList.add('errorMessage');
|
|
el.appendChild(div);
|
|
return div;
|
|
}
|
|
function getJSON(url, cb) {
|
|
let xhr = new XMLHttpRequest();
|
|
xhr.open('GET', baseUrl + url, true);
|
|
xhr.responseType = 'json';
|
|
xhr.onload = () => {
|
|
let status = xhr.status;
|
|
cb(status === 200 ? null : status, xhr.response);
|
|
}
|
|
xhr.onerror = (evt) => {
|
|
cb(xhr.status || 500, xhr.statusText);
|
|
}
|
|
xhr.send();
|
|
}
|
|
function putJSON(url, data, cb) {
|
|
let xhr = new XMLHttpRequest();
|
|
console.log({ put: baseUrl + url, data: data });
|
|
xhr.open('PUT', baseUrl + url, true);
|
|
xhr.responseType = 'json';
|
|
xhr.setRequestHeader('Content-Type', 'application/json; charset=utf-8');
|
|
xhr.setRequestHeader('Accept', 'application/json');
|
|
xhr.onload = () => {
|
|
let status = xhr.status;
|
|
cb(status === 200 ? null : status, xhr.response);
|
|
}
|
|
xhr.onerror = (evt) => {
|
|
cb(xhr.status || 500, xhr.statusText);
|
|
}
|
|
xhr.send(JSON.stringify(data));
|
|
}
|
|
|
|
function waitMessage(el) {
|
|
let div = document.createElement('div');
|
|
div.innerHTML = '<div class="lds-roller"><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div></div></div>';
|
|
div.classList.add('waitoverlay');
|
|
el.appendChild(div);
|
|
return div;
|
|
}
|
|
function clearErrors() {
|
|
let errors = document.querySelectorAll('div.errorMessage');
|
|
if (errors && errors.length > 0) errors.forEach((el) => { el.remove(); });
|
|
}
|
|
function saveSettings() {
|
|
let elems = document.querySelectorAll('[data-bind]');
|
|
let obj = {};
|
|
for (let i = 0; i < elems.length; i++) {
|
|
let el = elems[i];
|
|
switch (el.dataset.type) {
|
|
case 'bool':
|
|
obj[el.dataset.bind] = el.checked;
|
|
break;
|
|
case 'float':
|
|
obj[el.dataset.bind] = parseFloat(el.value);
|
|
break;
|
|
default:
|
|
obj[el.dataset.bind] = parseInt(el.value, 10);
|
|
break;
|
|
}
|
|
}
|
|
let overlay = waitMessage(document.getElementById('divFields'));
|
|
putJSON('/setRadioConfig', { config: obj }, (err, radio) => {
|
|
overlay.remove();
|
|
if (err) {
|
|
console.log(err);
|
|
serviceError(document.getElementById('divFields'), err);
|
|
}
|
|
else {
|
|
console.log(radio);
|
|
}
|
|
});
|
|
|
|
}
|
|
|
|
</script>
|
|
</head>
|
|
<body>
|
|
<div syle="white-space:nowrap;">
|
|
<label for="repairAddress">Repair Address</label>
|
|
<input id="fldRepairAddress" name="repairAddress" type="number" style="margin-right: 10px; text-align: right; display: inline-block; width: 127px;" value="4624451" />
|
|
<label for="repairRCode">Rolling Code</label>
|
|
<input id="fldRepairRCode" name="repairRCode" type="number" style="margin-right:10px;text-align:right;display:inline-block;width:127px;" value="1641" />
|
|
</div>
|
|
<div class="button-container">
|
|
<button id="btnRepairRemote" style="width:127px" onclick="runRepair = true; runRemoteRepair();">Repair Remote</button>
|
|
<button id="btnStopRepairRemote" style="width:127px" onclick="stopRemoteRepair();">Stop Repair</button>
|
|
</div>
|
|
<div id="divFields"></div>
|
|
<div class="button-container">
|
|
<button id="btnSaveSetting" type="button" style="display:inline-block;width:44%" onclick="resetDefaults();">
|
|
Reset Defaults
|
|
</button>
|
|
<button id="btnSaveSetting" type="button" style="display:inline-block;width:44%" onclick="saveSettings();">
|
|
Save Radio Settings
|
|
</button>
|
|
|
|
</div>
|
|
<script type="text/javascript">
|
|
createFields();
|
|
loadConfig();
|
|
</script>
|
|
</body>
|
|
</html>
|