Add extracted tools: CitrineOS, OpenOCPP, ShapeShifter

- CitrineOS core extracted (CSMS OCPP 2.0.1)
- OpenOCPP extracted (firmware OCPP 1.6J/2.0.1)
- ShapeShifter library installed (pip install -e)
- ShapeShifter specification extracted
- EVerest extracted

TODO updated with progress
This commit is contained in:
Eric F
2026-06-08 00:38:27 -04:00
parent 468cfeaa50
commit d398a6ced2
7326 changed files with 1177561 additions and 7 deletions

View File

@@ -0,0 +1 @@
*.gz

View File

@@ -0,0 +1,247 @@
<!DOCTYPE html>
<html>
<style>
.form-group {
margin-bottom: 10px;
}
.form-group label {
display: inline-block;
width: 280px; /* Adjust width as needed */
}
.form-group input {
width: 300px; /* Adjust width as needed */
}
.form-group input[type="checkbox"] {
width: auto; /* Let checkbox have natural width */
}
</style>
<body>
<h2 id="myTitle">Charger Configuration</h2>
<div>
<h3>Network Settings</h3>
<div class="form-group">
<label for="networkUrl">Central System URL (Slot 0):</label>
<input type="text" id="networkUrl" name="networkUrl" size="60">
</div>
<div class="form-group">
<label for="chargePointId">Charge Point ID:</label>
<input type="text" id="chargePointId" name="chargePointId" size="60">
</div>
<div class="form-group">
<label for="ocpp_protocol">OCPP Protocol:</label>
<select id="ocpp_protocol" name="ocpp_protocol">
<option value="OCPP 1.6" selected>OCPP 1.6</option>
<option value="OCPP 2.0.1">OCPP 2.0.1</option>
</select>
</div>
<div class="form-group">
<label for="securityProfile">Security Profile:</label>
<input type="number" id="securityProfile" name="securityProfile" size="60">
</div>
<div class="form-group">
<label for="clearCertificates">Clear certificates:</label>
<input type="checkbox" id="clearCertificates" name="clearCertificates">
</div>
</div>
<div>
<h3>Device Settings</h3>
<div class="form-group">
<label for="serialNumber">Serial Number:</label>
<input type="text" id="serialNumber" name="serialNumber" size="40">
</div>
<div class="form-group">
<label for="maxCurrent">Device Maximum Current (amps):</label>
<input type="number" id="maxCurrent" name="maxCurrent" size="40">
</div>
<div class="form-group">
<label for="vendor">Vendor:</label>
<input type="text" id="vendor" name="vendor" size="40">
</div>
<div class="form-group">
<label for="model">Model:</label>
<input type="text" id="model" name="model" size="40">
</div>
<div class="form-group">
<label for="numberOfConnectors">Number of Connectors:</label>
<input type="number" id="numberOfConnectors" name="numberOfConnectors" size="40">
</div>
</div>
<br>
<div>
<button onclick="submit()">Submit</button><br>
<p id="result" style="color:blue"> </p>
</div>
<hr>
<div>
<h3>OCPP 1.6 Configuration</h3>
<div class="form-group">
<label for="ocpp1_6_parameter">Select an OCPP Parameter:</label>
<select id="ocpp1_6_parameter" name="ocpp1_6_parameter" onchange="onOcppParameterChanged()"></select>
</div>
<div class="form-group">
<label for="ocpp1_6_parameter_input">Value:</label>
<input type="text" id="ocpp1_6_parameter_input">
</div>
<div class="form-group">
<label>OCPP Parameter Read-Only:</label>
<span id="ocpp-parameter-readonly-status">-</span>
</div>
<br>
<div>
<button id="ocpp1_6_submit_button" onclick="submitOcpp1_6_ParameterChange()">Submit Parameter Change</button>
<p id="ocpp1_6_submit_result" style="color:blue"> </p>
</div>
</div>
<script>
window.ocppParametersMap = window.ocppParametersMap || new Map();
fetchAndFillSettings();
fetchAndFillOcpp1_6_Parameter();
function fetchAndFillSettings() {
fetch("getDeviceConfiguration")
.then(response => response.json())
.then(payload => {
document.getElementById("networkUrl").value = payload.centralSystemUrl;
document.getElementById("chargePointId").value = payload.chargePointId;
let normalizedOcppProtocol = null;
if (payload.ocppProtocol != null) {
normalizedOcppProtocol = payload.ocppProtocol.replace(/^"(.*)"$/, "$1");
}
if (normalizedOcppProtocol == null || normalizedOcppProtocol === "OCPP20") {
document.getElementById("ocpp_protocol").value = "OCPP 2.0.1";
} else {
document.getElementById("ocpp_protocol").value = "OCPP 1.6";
}
document.getElementById("securityProfile").value = payload.securityProfile;
document.getElementById("serialNumber").value = payload.serialNumber;
document.getElementById("maxCurrent").value = payload.maxCurrentAmps;
document.getElementById("vendor").value = payload.vendor;
document.getElementById("model").value = payload.model;
document.getElementById("numberOfConnectors").value = payload.numberOfConnectors;
document.getElementById("myTitle").innerHTML = "Charger Configuration " + "(" + payload.firmwareVersion + ")";
});
}
function submit() {
var data = new Object();
data.centralSystemUrl = document.getElementById("networkUrl").value;
data.chargePointId = document.getElementById("chargePointId").value;
data.ocppProtocol = document.getElementById("ocpp_protocol").value === "OCPP 2.0.1" ? "OCPP20" : "OCPP16";
data.securityProfile = parseInt(document.getElementById("securityProfile").value);
data.clearCertificates = document.getElementById("clearCertificates").checked;
data.serialNumber = document.getElementById("serialNumber").value;
data.maxCurrentAmps = document.getElementById("maxCurrent").value;
data.vendor = document.getElementById("vendor").value;
data.model = document.getElementById("model").value;
data.numberOfConnectors = parseInt(document.getElementById("numberOfConnectors").value, 10);
data.firmwareVersion = "";
const jsonString = JSON.stringify(data);
console.log(jsonString);
fetch("updateDeviceConfiguration", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(data)
}).then(response => response.json())
.then(json => {
if (json.successful === true)
document.getElementById("result").innerHTML = "Configuration updated successfully";
else
document.getElementById("result").innerHTML = "Configuration update failed";
})
.catch((error) => {
document.getElementById("result").innerHTML = "Configuration update failed:" + error;
});
}
function fetchAndFillOcpp1_6_Parameter() {
fetch("getOcppParameters")
.then(response => response.json())
.then(payload => {
const configKeys = payload?.rsp?.configurationKey;
if (!configKeys) {
console.warn("No configuration keys found in response.");
return;
}
configKeys.forEach(item => {
window.ocppParametersMap.set(item.key, { value: item.value, readonly: item.readonly });
});
const select = document.getElementById("ocpp1_6_parameter");
select.innerHTML = Array.from(window.ocppParametersMap.keys())
.map(key => `<option value="${key}">${key}</option>`)
.join('');
onOcppParameterChanged();
})
.catch(e => {
console.error(e);
});
}
function onOcppParameterChanged() {
const selectedKey = document.getElementById("ocpp1_6_parameter").value;
const param = window.ocppParametersMap.get(selectedKey);
document.getElementById("ocpp1_6_parameter_input").value = param.value || "";
document.getElementById("ocpp-parameter-readonly-status").textContent = param.readonly ? "Yes (Read-Only)" : "No (Editable)";
}
async function submitOcpp1_6_ParameterChange() {
const selectedKey = document.getElementById("ocpp1_6_parameter").value;
const newValue = document.getElementById("ocpp1_6_parameter_input").value;
// Send data to server
try {
const response = await fetch('updateOcppParameter', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ key: selectedKey, value: newValue })
});
if (response.ok) {
const payload = await response.json();
console.log(`change ocpp parameter response: ${payload}`);
if (payload.status === "Accepted" || payload.status === "RebootRequired") {
if (ocppParametersMap.has(selectedKey)) {
window.ocppParametersMap.get(selectedKey).value = newValue;
} else {
console.warn(`Key ${selectedKey} not found in ocppParametersMap.`);
}
}
document.getElementById("ocpp1_6_submit_result").innerHTML = `Get response with status: ${payload.status} for ${selectedKey} = ${newValue}`;
} else {
document.getElementById("ocpp1_6_submit_result").innerHTML = "Failed to update the ocpp variable value on server.";
}
} catch (error) {
console.error("Error submitting value:", error);
document.getElementById("ocpp1_6_submit_result").innerHTML = "An error occurred while updating the ocpp variable.";
}
}
</script>
</body>
</html>

View File

@@ -0,0 +1,244 @@
<!DOCTYPE html>
<html>
<head>
<style>
.left {
position: relative;
top: 10px;
left: 40px;
width: 800px;
border: 0px solid #73AD21;
padding: 0px;
}
.eighteen-point {line-height: 18pt;}
.li-position
{
left: 30px;
position: relative;
}
.flex-container {
display: flex;
}
.flex-child {
flex: 0.5;
}
</style>
</head>
<body>
<div>
<h2>
<label style="color:green" for="connector_id">Connector Id:</label>
<select id="connector_id" name="connector_id" onchange="onConnectorIdChanged()">
</select>
</h2>
</div>
<div>
<h2>EV connection</h2>
<input onchange="updateChanges()" type="radio" id="ev_connected_false" name="ev_connected" checked>EV disconnected</input><br>
<input onchange="updateChanges()" type="radio" id="ev_connected_true" name="ev_connected">EV connected</input><br>
<h2>Suspended by EV</h2>
<input onchange="updateChanges()" type="radio" id="suspended_ev_true" name="suspended_ev" checked>Suspended by EV</input><br>
<input onchange="updateChanges()" type="radio" id="suspended_ev_false" name="suspended_ev">EV drawing power</input><br>
<h2>Suspended by charger</h2>
<input onchange="updateChanges()" type="radio" id="suspended_charger_true" name="suspended_charger" checked>Suspended by charger</input><br>
<input onchange="updateChanges()" type="radio" id="suspended_charger_false" name="suspended_charger">Charger delivering power</input><br>
<h2>RFID tap</h2>
<label for="rfid_token">Token:</label>
<input type="text" id="rfid_token" name="rfid_token" size="40"><br>
<label for="rfid_type">Type:</label>
<select name="rfid_type" id="rfid_type" style="margin-top: 10px">
<option value="Central">Central</option>
<option value="eMAID">eMAID</option>
<option value="ISO14443">ISO14443</option>
<option value="ISO15693">ISO15693</option>
<option value="KeyCode">KeyCode</option>
<option value="Local">Local</option>
<option value="MacAddress">MacAddress</option>
<option value="kNoAuthorization">kNoAuthorization</option>
</select>
<button onclick="onSendRFID()">Simulate RFID interaction</button><br>
<h2>Websocket connection</h2>
<input onchange="updateChanges()" type="radio" id="allow_websocket_connection_true" name="allow_websocket_connection" checked>Allow connection</input><br>
<input onchange="updateChanges()" type="radio" id="allow_websocket_connection_false" name="allow_websocket_connection">Disallow connection</input><br>
</div>
<br>
<hr>
<div class="flex-container">
<div class="flex-child">
<h2>Charging status</h2>
<label for="charging_status">Charging state:</label>
<strong id="charging_status" name="charging_status"></strong>
</div>
</div>
<br>
<div>
<br>
<p id="message_result" style="color:blue"></p>
</div>
<script>
var simulateRfidRequest = null;
var updateConnectorSettingsRequest = null;
var lastStatusUpdate = null;
// document.body.addEventListener('change', function(e) {
// if (e.target.id === "") {
//
// }
// if (e.target.type === "select-one") {
// document.getElementById("message_result").innerHTML = "";
// return;
// }
// updateChanges();
// });
setTimeout(() => {
fetch("getDeviceConfiguration", {signal: AbortSignal.timeout(1000)})
.then(response => response.text())
.then(text => {
let obj = JSON.parse(text);
let number_of_connectors = (obj || {})['numberOfConnectors'] || 0;
let connector_id_select = document.getElementById("connector_id");
for (let i = 0; i < number_of_connectors; i++) {
connector_id_select.options[i] = new Option(i+1, i+1);
}
})
.then(() => {
setTimeout(fetchAndFill, 100);
})
}, 100);
function onConnectorIdChanged() {
lastStatusUpdate = null;
}
function updateBoolInput(name, value) {
if (value === true) {
if (!document.getElementById(name + "_true").checked) {
console.log("Setting: " + name + "_true");
document.getElementById(name + "_true").checked = true;
}
} else {
if (!document.getElementById(name + "_false").checked) {
console.log("Setting: " + name + "_false");
document.getElementById(name + "_false").checked = true;
}
}
}
function fetchAndFill() {
let future;
if (updateConnectorSettingsRequest != null) {
future = fetch("updateConnectorSettings", {
signal: AbortSignal.timeout(1000),
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(updateConnectorSettingsRequest)
})
.then(()=>{
document.getElementById("message_result").innerHTML = "The charger status was updated successfully.";
})
.catch(error => {
document.getElementById("message_result").innerHTML = "The charger status was not updated due to error:" + error;
});
updateConnectorSettingsRequest = null;
} else if (simulateRfidRequest != null) {
future = fetch("simulateRfidInteraction", {
signal: AbortSignal.timeout(1000),
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(simulateRfidRequest)
})
.then(()=>{
document.getElementById("message_result").innerHTML = "RFID request accepted.";
})
.catch(error => {
document.getElementById("message_result").innerHTML = "RFID request failed with error: " + error;
});
simulateRfidRequest = null;
} else if (lastStatusUpdate == null || Math.abs(Date.now() - lastStatusUpdate) > 2000) {
lastStatusUpdate = Date.now();
connector_id = document.getElementById("connector_id").value;
if (connector_id === null) {
connector_id = 1;
}
future = fetch("getConnectorStatus" + "?connectorId=" + connector_id, {signal: AbortSignal.timeout(1000)})
.then(response => response.text())
.then(text => {
let obj = JSON.parse(text);
let settings = (obj || {})['connectorSettings'] || {};
updateBoolInput("ev_connected", settings['vehicleConnected']);
updateBoolInput("suspended_ev", settings['suspendedByVehicle']);
updateBoolInput("suspended_charger", settings['suspendedByCharger']);
let charging_status = (obj || {})['chargePointStatus1_6'] || {};
const chargingElement = document.getElementById('charging_status');
if (charging_status === "ValueNotFoundInEnum") {
chargingElement.innerHTML = "";
} else {
chargingElement.innerHTML = charging_status;
// Set color based on charging_status
switch (charging_status) {
case "Charging":
chargingElement.style.color = "green";
break;
case "Faulted":
chargingElement.style.color = "red";
break;
case "Unavailable":
chargingElement.style.color = "gray";
break;
default:
chargingElement.style.color = "blue"; // Default color
break;
}
}
});
} else {
future = Promise.resolve();
}
future.then(() => setTimeout(fetchAndFill, 100))
.catch(() => setTimeout(fetchAndFill, 100));
}
function updateChanges() {
updateConnectorSettingsRequest = {
connectorId: parseInt(document.getElementById("connector_id").value, 10),
vehicleConnected: document.getElementById("ev_connected_true").checked,
suspendedByVehicle: document.getElementById("suspended_ev_true").checked,
suspendedByCharger: document.getElementById("suspended_charger_true").checked,
allowWebsocketConnection: document.getElementById("allow_websocket_connection_true").checked
};
}
function onSendRFID() {
simulateRfidRequest = {
idToken: document.getElementById("rfid_token").value,
tokenType: document.getElementById("rfid_type").value
};
}
</script>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,121 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body {
margin: 0;
font-family: Trebuchet MS, Tahoma, Arial, Helvetica, sans-serif;
}
.topnav {
overflow: hidden;
background-color: #333;
}
.topnav a {
float: left;
color: #f2f2f2;
text-align: center;
padding: 14px 16px;
text-decoration: none;
font-size: 17px;
}
.topnav a:hover {
background-color: #ddd;
color: black;
}
.topnav a.active {
background-color: #04AA6D;
color: white;
}
</style>
</head>
<body>
<div class="topnav">
<a id="href_setup" class="mylink" href="#" onclick=loadSetup()>Setup</a>
<a id="href_config" class="mylink" href="#" onclick=loadConfig()>Configuration</a>
<a id="href_control" class="mylink" href="#" onclick=loadControl()>Control</a>
</div>
<div id="content" style="padding-left:16px">
</div>
<script>
addSetLinkActiveEvents();
document.getElementById('href_setup').click();
function addSetLinkActiveEvents() {
const links = document.querySelectorAll(".mylink");
if (links.length) {
links.forEach((link) => {
link.addEventListener('click', (e) => {
links.forEach((link) => {
link.classList.remove('active');
});
e.preventDefault();
link.classList.add('active');
});
});
}
}
function loadConfig() {
fetch("/config")
.then(response => response.text())
.then(text => {
document.getElementById("content").innerHTML = text;
executeScriptElements(document.getElementById("content"));
document.title = "Charger Simulator - Configuration";
})
.catch(error => {
document.getElementById("content").innerHTML = error;
});
}
function loadControl() {
fetch("/control")
.then(response => response.text())
.then(text => {
document.getElementById("content").innerHTML = text;
executeScriptElements(document.getElementById("content"));
document.title = "Charger Simulator - Control";
})
.catch(error => {
document.getElementById("content").innerHTML = error;
});
}
function loadSetup() {
fetch("/setup")
.then(response => response.text())
.then(text => {
document.getElementById("content").innerHTML = text;
executeScriptElements(document.getElementById("content"));
document.title = "Charger Simulator - Setup";
})
.catch(error => {
document.getElementById("content").innerHTML = error;
});
}
function executeScriptElements(containerElement) {
const scriptElements = containerElement.querySelectorAll("script");
Array.from(scriptElements).forEach((scriptElement) => {
const clonedElement = document.createElement("script");
Array.from(scriptElement.attributes).forEach((attribute) => {
clonedElement.setAttribute(attribute.name, attribute.value);
});
clonedElement.text = scriptElement.text;
scriptElement.parentNode.replaceChild(clonedElement, scriptElement);
});
}
</script>
</body>
</html>

View File

@@ -0,0 +1,495 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Test Charger</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<meta http-equiv="Content-Security-Policy" content="script-src 'unsafe-inline'; style-src 'unsafe-inline'">
<style type="text/css">
body {
font-family: Trebuchet MS, Tahoma, Arial, Helvetica, sans-serif;
}
input[type=text],
input[type=password] {
width: 100%;
padding: 12px 20px;
margin: 8px 0;
display: inline-block;
border: 1px solid #ccc;
box-sizing: border-box;
}
button {
background-color: #4CAF50;
color: white;
padding: 14px 20px;
margin: 8px 0;
border: none;
cursor: pointer;
width: 100%;
}
button:hover {
opacity: 0.8;
}
.cancelbtn {
width: auto;
padding: 10px 18px;
background-color: #f44336;
}
.imgcontainer {
text-align: center;
margin: 24px 0 12px 0;
position: relative;
}
img.avatar {
width: 40%;
border-radius: 50%;
}
.container {
padding: 16px;
}
span.psw {
float: right;
padding-top: 16px;
}
.modal {
display: none;
position: fixed;
z-index: 1;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgb(0, 0, 0);
background-color: rgba(0, 0, 0, 0.4);
padding-top: 60px;
}
.modal-content {
background-color: #fefefe;
margin: 5% auto 15% auto;
border: 1px solid #888;
width: 80%;
}
.close {
position: absolute;
right: 25px;
top: 0;
color: #000;
font-size: 35px;
font-weight: bold;
}
.close:hover,
.close:focus {
color: red;
cursor: pointer;
}
.animate {
-webkit-animation: animatezoom 0.6s;
animation: animatezoom 0.6s
}
@-webkit-keyframes animatezoom {
from {
-webkit-transform: scale(0)
}
to {
-webkit-transform: scale(1)
}
}
@keyframes animatezoom {
from {
transform: scale(0)
}
to {
transform: scale(1)
}
}
@media screen and (max-width: 300px) {
span.psw {
display: block;
float: none;
}
.cancelbtn {
width: 100%;
}
}
h3 {
font-style: normal;
font-weight: normal;
font-size: 14px;
line-height: 20px;
}
.blue {
font-style: normal;
font-weight: bold;
font-size: 14px;
line-height: 20px;
color: #5DA2E1;
}
h2 {
font-style: normal;
font-weight: 600;
font-size: 28px;
line-height: 36px;
display: flex;
align-items: center;
color: #1F1F1F;
}
.form-group {
margin-top: 16px;
}
label {
font-style: normal;
font-weight: 600;
font-size: 16px;
line-height: 22px;
display: flex;
align-items: flex-end;
color: #1F1F1F;
}
select {
height: 48px;
width: 100%;
border: none;
background-color: #F5F6F8;
border-radius: 4px;
font-style: normal;
font-weight: normal;
font-size: 16px;
line-height: 24px;
color: #ADADAD;
padding: 12px 20px;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background-image: url(data:image/svg+xml;base64,PHN2ZyBmaWxsPSdibGFjaycgaGVpZ2h0PScyNCcgdmlld0JveD0nMCAwIDI0IDI0JyB3aWR0aD0nMjQnIHhtbG5zPSdodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2Zyc+PHBhdGggZD0nTTcgMTBsNSA1IDUtNXonLz48cGF0aCBkPSdNMCAwaDI0djI0SDB6JyBmaWxsPSdub25lJy8+PC9zdmc+);
background-repeat: no-repeat;
background-position-x: 97%;
background-position-y: 12px;
}
.black {
color: #1F1F1F;
}
.form-control {
margin-top: 4px;
}
.bolden {
font-family: "Arial Black"
}
input[type='password'],
input[type='text'] {
border: none;
background-color: #F5F6F8;
border-radius: 4px;
height: 48px;
font-style: normal;
font-weight: normal;
font-size: 16px;
line-height: 24px;
color: #ADADAD;
background-repeat: no-repeat;
background-position-x: 95%;
background-position-y: 12px;
background-size: 22px;
}
input:not(:placeholder-shown) {
color: black !important;
}
.eye {
width: 20px;
position: absolute;
right: 40px;
height: 60px;
background: no-repeat url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABDUlEQVQ4jd2SvW3DMBBGbwQVKlyo4BGC4FKFS4+TATKCNxAggkeoSpHSRQbwAB7AA7hQoUKFLH6E2qQQHfgHdpo0yQHX8T3exyPR/ytlQ8kOhgV7FvSx9+xglA3lM3DBgh0LPn/onbJhcQ0bv2SHlgVgQa/suFHVkCg7bm5gzB2OyvjlDFdDcoa19etZMN8Qp7oUDPEM2KFV1ZAQO2zPMBERO7Ra4JQNpRa4K4FDS0R0IdneCbQLb4/zh/c7QdH4NL40tPXrovFpjHQr6PJ6yr5hQV80PiUiIm1OKxZ0LICS8TWvpyyOf2DBQQtcXk8Zi3+JcKfNafVsjZ0WfGgJlZZQxZjdwzX+ykf6u/UF0Fwo5Apfcq8AAAAASUVORK5CYII=);
filter: brightness(0);
background-position: center;
cursor: pointer;
}
.eye-hide {
width: 20px;
position: absolute;
right: 40px;
height: 65px;
background: no-repeat url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA4ZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQ1IDc5LjE2MzQ5OSwgMjAxOC8wOC8xMy0xNjo0MDoyMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDpjYWVjOWZiYi00ZDU4LTQxMmYtODcwMi0zYWMxYzc4ZTU0MWQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QjNDQTYwNTJFRjM3MTFFOEI1MDlFMjg4NDlCM0IyMjIiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QjNDQTYwNTFFRjM3MTFFOEI1MDlFMjg4NDlCM0IyMjIiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKE1hY2ludG9zaCkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpkMTgzYmMzOC1mMzlmLTQ2OTQtYjI3ZC0xOWVhZDBjN2RhZWMiIHN0UmVmOmRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDpjNzAwNzcyYi0yYzg4LWQzNDMtOTMwNi03ZjAyZDE2ZTBjOTgiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz53I3B2AAABVklEQVR42sTTyytFURTH8XOFzMRA3RQDlCITQqSk/AMmkomBx+QWE8XAUIqZZEJedVMkAymPMkCuN3kUEckjUoqJCY7v0u/oZHoGdn3q7NNea6+99jkh13WdICPOCThCf+ZJKEAtqhHGJ26xjllsIgMN2PcH28shBdi5jtGFHc1dJWnGFZ7Q6QXn4QBHuMQ2itCGGzxjHGP4wioqveCwgueRq7LqMapd99CrRHdoRbdiMq0Hw9rNMqZiDVtKZuUnoA6LqszWx2Nax3Q+EFE1aVjBKfpxjQcMKOkJ0rW2SbE/Z7Mys3QrNZhT+TMYwYvm7QpOQQxRr/uH6EGLdr3HIM58N9CHRCRjAefWA6+RZVhSh62iHOSjQ00r0bpynfsChV6wlfWojyWi0mJ6rlJzG7GMV0wh2/8l2plLManmVegai1WujTfsYgIbeP9N8O8/U+AE3wIMAOAeYTJHg1SWAAAAAElFTkSuQmCC);
filter: brightness(0);
background-position: center;
cursor: pointer;
}
select::-ms-expand {
display: none;
}
input:focus,
select:focus {
border: solid 1px #000 !important;
outline: none;
}
input[type='submit'] {
width: 100%;
height: 56px;
background: #5DA2E1;
border-radius: 4px;
padding: 16px 20px;
border: none;
font-style: normal;
font-weight: 600;
font-size: 18px;
line-height: 24px;
color: white !important;
}
.error {
color: red;
font-size: 11px;
display: none;
}
.mb-12 {
margin-bottom: 12px;
}
.mb-8 {
margin-bottom: 8px;
}
.mt {
margin-top: 30px;
}
</style>
<script>
function _el(s) { return document.getElementById(s); }
window.onclick = function (event) {
var el = _el('id01');
if (event.target == el) {
el.style.display = "none";
}
}
var u = 'http://', z = "********", z1 = "", prev_el = null, u1 = 'acharger.ca';
function sendReq(r, cb) {
var x = new XMLHttpRequest();
x.onreadystatechange = function () {
if (this.readyState == 4 && (this.status == 200 || this.status == 0)) {
if (cb) cb(this.responseText);
}
};
x.open("GET", r, true);
x.send();
}
function liclk(e) {
if (e && e.innerText) {
var q = e.innerText.split(" "), t = e.style;
if (q.length >= 3) {
z1 = q[0];
var el = _el("uname");
el.value = z1;
el = _el("m_si");
el.value = z;
if (t) {
t.backgroundColor = "#00FFBF";
if (prev_el)
prev_el.backgroundColor = "#FFFFFF";
prev_el = t;
}
}
}
}
function checkFormValidation() {
document.getElementById("networkUrlError").style.display = "none";
document.getElementById("ocppIdError").style.display = "none";
var valid = true;
let password = document.getElementById("Password").value;
let confirmPassword = document.getElementById("Confirmpassword").value;
if (password !== "" && password !== confirmPassword) {
document.getElementById("PasswordMatchError").style.display = "block";
valid = false;
} else {
document.getElementById("PasswordMatchError").style.display = "none";
}
if (document.getElementById("networkUrl").value == "") {
document.getElementById("networkUrlError").style.display = "block";
valid = false;
}
if (document.getElementById("ocppId").value == "") {
document.getElementById("ocppIdError").style.display = "block";
valid = false;
}
return valid;
}
function submitData() {
if (!checkFormValidation()) {
return;
}
fetch("submitSetup", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
ssid: document.getElementById("wifi").value,
password: document.getElementById("Password").value,
networkUrl: document.getElementById("networkUrl").value,
ocppId: document.getElementById("ocppId").value,
ocppProtocol: document.getElementById("ocppProtocol").value
})
})
.then(()=>{
document.getElementById("step1").style.display = "none";
document.getElementById("step2").style.display = "block";
})
.catch(error => {
console.log("Submit failed with error: ", error);
});
}
function toggleShowPassword() {
if (document.getElementById('PasswordEye').className === "eye-hide") {
document.getElementById('Password').type = "text";
document.getElementById('Confirmpassword').type = "text";
document.getElementById('PasswordEye').className = "eye";
document.getElementById('ConfirmpasswordEye').className = "eye";
} else {
document.getElementById('Password').type = "password";
document.getElementById('Confirmpassword').type = "password";
document.getElementById('PasswordEye').className = "eye-hide"
document.getElementById('ConfirmpasswordEye').className = "eye-hide";
}
}
sendReq("networks", function (s) {
const networks = JSON.parse(s);
console.log(networks);
if (networks) {
var select = document.getElementById('wifi');
var opt = document.createElement('option');
opt.value = "";
opt.innerHTML = "WiFi name";
select.appendChild(opt);
for (let i = 0; i < networks.length; i++) {
const entry = networks[i];
var opt = document.createElement('option');
opt.value = entry.ssid;
opt.innerHTML = entry.ssid;
select.appendChild(opt);
}
}
});
</script>
</head>
<body>
<div id="step2" style="display: none;">
<h2>Charger successfully updated </h2>
<h3>Your charger's settings have been updated. </h3>
<h3>You may now close this web page.</h3>
</div>
<div id="step1">
<h2 class="mb-12">Test Charger setup</h2>
<div class="blue">*Case sensitive</div>
<div class="form-group">
<label>WiFi name</label>
<div class="form-control">
<select onchange="javascript:wifiChanged(this);checkFormValidation('wifi')" id="wifi"
name="wifi"></select>
<span id="wifiError" class="error">Wifi name is required!</span>
</div>
</div>
<div class="form-group">
<label>Password</label>
<div class="form-control">
<input id="Password" onblur="javascript:checkFormValidation()" name="Password" type="password"
placeholder="Password">
<span class="eye-hide" id="PasswordEye" onclick="javascript:toggleShowPassword()"></span>
<span id="PasswordError" class="error">Password is required!</span>
</div>
</div>
<div class="form-group">
<label>Confirm password</label>
<div class="form-control">
<input id="Confirmpassword" onblur="javascript:checkFormValidation()"
name="Confirmpassword" type="password" placeholder="Confirm password">
<span class="eye-hide" id="ConfirmpasswordEye"
onclick="javascript:toggleShowPassword()"></span>
<span id="ConfirmpasswordError" class="error">Confirm password is required!</span>
<span id="PasswordMatchError" class="error">Password and Confirm password does not match!</span>
</div>
</div>
<div class="form-group" id="divNetworkUrl" style="display: block;">
<label>Enter network URL</label>
<div class="form-control">
<input type="text" onblur="javascript:checkFormValidation()" placeholder="Enter network URL" id="networkUrl" name="networkUrl">
<span id="networkUrlError" class="error">Network URL is required!</span>
</div>
</div>
<div class="form-group" id="divOcppId" style="display: block;">
<label>Enter OCPP ID</label>
<div class="form-control">
<input type="text" onblur="javascript:checkFormValidation()" placeholder="Enter OCPP ID" id="ocppId" name="ocppId">
<span id="ocppIdError" class="error">OCPP ID is required!</span>
</div>
</div>
<div class="form-group" id="divOcppProtocol" style="display: block;">
<label>Select OCPP protocol</label>
<div class="form-control">
<select id="ocppProtocol" name="ocppProtocol">
<option value="OCPP16">OCPP 1.6</option>
<option value="OCPP20" selected>OCPP 2.0.1</option>
</select>
</div>
</div>
<div class="form-group mt">
<input type="submit" onclick="javascript:submitData()" value="Submit" />
</div>
</div>
</body>
</html>