bt-seneca-msc
Version:
A pure Javascript API for the Seneca Multi Smart Calibrator (MSC) device, using web bluetooth.
307 lines (288 loc) • 12.4 kB
HTML
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Seneca MSC</title>
<script src="./dist/bt-seneca-msc.js"></script>
<!-- CSS only -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-0evHe/X+R7YkIZDRvuzKMRqM+OrBnVFBL6DOitfPri4tjfHxaWutUpFmBp4vmVor" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.6.0.min.js"
integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
</head>
<body>
<h2>API commands</h2>
<div class="container container-md">
<div class="row">
<div class="col">
<div class="container d-grid gap-3">
<div class="row">
<div class="col">
<button id="startBt" class="btn btn-primary">Pair(false)</button>
<button id="startBt2" class="btn btn-primary">Pair(true)</button>
</div>
<div class="col">
<div id="resultPair"></div>
</div>
</div>
<div class="row">
<div class="col">
<button id="stopBt" class="btn btn-primary">Stop()</button>
</div>
<div class="col">
<div id="resultStop"></div>
</div>
</div>
<div class="row">
<div class="col">
<button id="getState" class="btn btn-primary">GetState()</button>
</div>
<div class="col">
<div id="resultGetState"></div>
</div>
</div>
</div>
</div>
<div class="col">
<div class="container d-grid gap-3">
<div class="col">
<div class="row">
<label for="new_command_id">Command type</label>
<select id="new_command_id" class="form-control"></select>
</div>
<div class="row">
<label for="new_command_sp" id="setpoint1_desc">Setpoint 1</label>
<input type="number" id="new_command_sp" class="form-control" />
</div>
<div class="row">
<label for="new_command_sp2" id="setpoint2_desc">Setpoint 2</label>
<input type="number" id="new_command_sp2" class="form-control" />
</div>
<div class="row">
<input type="button" id="executeBtn" value="Execute(command)" class="btn btn-primary">
<div id="resultExecute"></div>
</div>
</div>
</div>
</div>
<div class="col">
<div class="container d-grid gap-3">
<div class="row">
<input type="button" id="traceBtn" value="Loglevel: debug"
onclick="MSC.log.setLevel(MSC.log.levels.TRACE);" class="btn btn-info">
</div>
<div class="row">
<input type="button" id="defaultLog" value="Loglevel: info"
onclick="MSC.log.setLevel(MSC.log.levels.INFO);" class="btn btn-info">
</div>
<div class="row">
<input type="button" id="defaultLog" value="Loglevel: default"
onclick="MSC.log.setLevel(MSC.log.levels.WARN);" class="btn btn-info">
</div>
</div>
</div>
</div>
<div class="row">
<div class="col">
<input type="button" id="jsonTraces" value="Test modbus traces" class="btn btn-info">
<div id="jsonTestTraces"></div>
</div>
</div>
</div>
<h2>GetState()</h2>
<div class="container">
<div class="row">
<div class="col col-md">
<table id="state" class="table table-striped">
<tr>
<td>Overall status</td>
<td>
<div id="overall"></div>
</td>
</tr>
<tr>
<td>Detailed status</td>
<td>
<div id="status"></div>
</td>
</tr>
<tr>
<td>Meter mode</td>
<td>
<div id="mode"></div>
</td>
</tr>
<tr>
<td>Last setpoint</td>
<td>
<div id="sp"></div>
</td>
</tr>
<tr>
<td>Last measure</td>
<td>
<div id="meas"></div>
</td>
</tr>
<tr>
<td>Bluetooth info</td>
<td>
<div id="btinfo"></div>
</td>
</tr>
<tr>
<td>Meter serial number</td>
<td>
<div id="serial"></div>
</td>
</tr>
<tr>
<td>Meter battery (V)</td>
<td>
<div id="battery"></div>
</td>
</tr>
<tr>
<td>Stats</td>
<td>
<div id="stats"></div>
</td>
</tr>
</table>
</div>
<div class="col col-sm">
<img src="./docs/msc.jpg" alt="Seneca MSC" width="400" />
</div>
</div>
</div>
<script>
// Event listeners
$("#jsonTraces").click(async () => {
var trace = MSC.GetJsonTraces();
$("#jsonTestTraces").text(trace);
})
$("#startBt").click(async () => {
$("#resultPair").text("Pending...");
var res = await MSC.Pair(false);
$("#resultPair").text("Result PAIR(false): " + res);
});
$("#startBt2").click(async () => {
$("#resultPair").text("Pending...");
var res = await MSC.Pair(true);
$("#resultPair").text("Result PAIR(true): " + res);
});
$("#stopBt").click(async () => {
$("#resultStop").text("Pending...");
var state = await MSC.GetState();
if (state.ready)
var stop = await MSC.Execute(new MSC.Command(MSC.CommandType.OFF)); // Stop the meter first
var res = await MSC.Stop(); // Then close interface
$("#resultStop").text("Result STOP: " + res);
});
$("#executeBtn").click(async () => {
await addRequest();
});
$("#getState").click(async () => {
$("#resultGetState").text("Pending...");
var res = await MSC.GetState();
$("#resultGetState").text("Result GetState: " + res);
});
// Called to enable/disable setpoints depending on commands
function configureSetpoints(ctype) {
// Create a temporary command
const command = new MSC.Command(ctype);
// Get the default setpoint for this command type
const setpoints = command.defaultSetpoint();
switch (Object.keys(setpoints).length) {
case 0:
$("#new_command_sp").addClass('d-none');
$("#new_command_sp2").addClass('d-none');
$("#setpoint1_desc").addClass('d-none');
$("#setpoint2_desc").addClass('d-none');
$("#setpoint1_desc").text('Setpoint 1');
$("#setpoint2_desc").text('Setpoint 2');
break;
case 1:
$("#new_command_sp").removeClass('d-none');
$("#new_command_sp2").addClass('d-none');
$("#setpoint1_desc").text(Object.keys(setpoints)[0]);
$("#setpoint1_desc").removeClass('d-none');
$("#setpoint2_desc").addClass('d-none');
break;
case 2:
$("#new_command_sp").removeClass('d-none');
$("#new_command_sp2").removeClass('d-none');
$("#setpoint1_desc").text(Object.keys(setpoints)[0]);
$("#setpoint2_desc").text(Object.keys(setpoints)[1]);
$("#setpoint1_desc").removeClass('d-none');
$("#setpoint2_desc").removeClass('d-none');
break;
default:
throw new Error("Not handled:" + setpoints.length);
}
}
$("#new_command_id").change(() => {
const value = $("#new_command_id").val();
configureSetpoints(value);
});
// Fill the command select items with available commands
var select = $("#new_command_id");
for (var i in MSC.CommandType) { // Will enumerate keys
// Skip reserved commands
var cmd = new MSC.Command(MSC.CommandType[i]);
if (!cmd.isValid())
continue;
var opt = $("<option>").val(MSC.CommandType[i]).text(i);
select.append(opt);
}
configureSetpoints($("#new_command_id").val());
function dumpArray(arr) {
var elem = "";
for (var key in arr) {
elem += key + " = " + arr[key] + "<br/>";
}
return elem;
}
async function addRequest() {
var type = parseInt($("#new_command_id").val());
var setpoint1 = $("#new_command_sp").val();
var setpoint2 = $("#new_command_sp2").val();
var comm;
if ($("#new_command_sp").hasClass('d-none')) {
comm = MSC.Command.CreateNoSP(type);
}
else if ($("#new_command_sp2").hasClass('d-none')) {
comm = MSC.Command.CreateOneSP(type, setpoint1);
} else {
comm = MSC.Command.CreateTwoSP(type, setpoint1, setpoint2);
}
$("#resultExecute").text("Pending...");
var res = await MSC.Execute(comm);
$("#resultExecute").text("Result : " + res);
}
async function updateStatus() {
var mstate = await MSC.GetState();
$("#overall").text("ready:" + mstate.ready + ", initializing:" + mstate.initializing);
$("#status").text(mstate.status);
$("#sp").html(dumpArray(mstate.lastSetpoint));
$("#meas").html(dumpArray(mstate.lastMeasure));
$("#btinfo").text(mstate.deviceName);
$("#serial").text(mstate.deviceSerial);
var description = "";
for (x in MSC.CommandType) {
if (MSC.CommandType[x] === mstate.deviceMode) {
description = x;
break;
}
}
$("#mode").text(mstate.deviceMode + " (" + description + ")");
$("#battery").text(mstate.batteryLevel);
$("#stats").html(dumpArray(mstate.stats));
setTimeout(updateStatus, 500);
}
setTimeout(updateStatus, 500);
</script>
</body>
</html>