Last active
April 30, 2020 12:13
-
-
Save blakadder/beb92969e5af570dfb34d0001dee4754 to your computer and use it in GitHub Desktop.
User script for automatic power calibration
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name tasmota-calibration | |
// @namespace http://tampermonkey.net/ | |
// @version 1.0 | |
// @description try to take over the world! | |
// @author yu_ya45 | |
// @match http://*/ | |
// @grant none | |
// ==/UserScript== | |
//----- Read me ----- | |
//This is a user script that automatically and accurately calibrates. | |
//(The voltage needs to be adjusted manually.) | |
//Device and version used for testing | |
//Sonoff S31 / Tasmota 6.4.1.21 | |
// | |
//Step1 | |
//Set each variable according to your environment in the Settings section. | |
// | |
//Step2 | |
//Apply this user script. | |
//After that, "Calibration Start" is added to the top page. | |
// | |
//Step3 | |
//If you have a multimeter, use the VoltageSet command to adjust the voltage. | |
// | |
//Step4 | |
//When you are ready and run "Calibration Start", calibration will be performed automatically. | |
//When starting the calibration, execute the following commands necessary to | |
//obtain the correct calibration value as an initial setting. | |
//Backlog VoltRes 3; WattRes 3; CurrentSet (expected current + 100mA) | |
//(The reason for executing the CurrentSet command is that when the power factor | |
//is 1, the power value is truncated.) | |
//Calibration is performed in the order of Power and Current. | |
//If you want to cancel the process, either press Start again or refresh the page. | |
// | |
//Step5 | |
//It is complete when "Calibration Done!" Is displayed. | |
// | |
//The reason why the power factor is not 100% | |
//The power factor of the incandescent bulb is 1, but adjusting the current to match it may result | |
//in a slight downward correction of the power. | |
//This is because when the measured active power is larger than the apparent power multiplied | |
//by the voltage value and the current value, the smaller apparent power value is treated as the active power. | |
//Therefore, this user script adjusts the current so that the probability of becoming | |
//"active power == apparent power" is around 50%. | |
//-----Settings start----- | |
//match address | |
//Please specify part of the address to which this user script applies. | |
var matchAddress = "192.168" | |
//Rated voltage(unit:V) | |
//Rated voltage of light bulb used for test | |
var ratedVoltage = 220; | |
//Rated power(unit:W) | |
//Rated power consumption of the light bulb used in the test | |
var ratedPower = 60; | |
//Calibration accuracy power range (unit:W) | |
//In the case of 0.05, adjust it to fall within | |
//the range of -0.05W to +0.05W compared to the expected value. | |
var accuracyPowerRange = 0.05; | |
//Calibration accuracy current range (unit:%) | |
//In case of 10, adjust the probability that "active power == apparent power" to be 40%-60%. | |
var accuracyCurrentRange = 10; | |
//Number of samplings | |
//How many sampling numbers do you use to calculate the average of expected error? | |
var samplingsPower = 50; | |
var samplingsCurrent = 50; | |
//-----Settings end----- | |
if(location.href.indexOf(matchAddress) == -1){ | |
return; | |
} | |
var x=null,lt,to,tp,pc=''; | |
var isInitPower = true; | |
var diffPowerSum = 0; | |
var diffPowerCnt = 0; | |
var expectedCurrent=ratedPower/ratedVoltage; | |
var expectedResistance=ratedVoltage/expectedCurrent; | |
var averageDiffPower; | |
var prevVal = ""; | |
var calibrationStatus = 0; | |
var isDelay = false; | |
var powerSetCal = 0; | |
var isInitCurrent = true; | |
var activePower = 0; | |
var apparentPower = 0; | |
var activePowerMinCnt = 0; | |
var apparentPowerMinCnt = 0; | |
var activePowerRate = 0; | |
var apparentPowerRate = 0; | |
var allCnt = 0; | |
//Add a button to start calibration. | |
var btn1 = document.createElement('button'); | |
btn1.textContent = 'Calibration Start'; | |
btn1.onclick = function (){ | |
if(calibrationStatus == 0){ | |
calibrationStatus = 1; | |
diffPowerSum = 0; | |
diffPowerCnt = 0; | |
}else{ | |
calibrationStatus = 0; | |
} | |
}; | |
var p1 = document.createElement('p'); | |
p1.appendChild(btn1); | |
var node = eb("l1"); | |
node.parentNode.insertBefore(p1, node); | |
//override method | |
window.eb = function eb(s){ | |
return document.getElementById(s); | |
} | |
window.la = function la(p){ | |
var a=''; | |
if(la.arguments.length==1){ | |
a=p; | |
clearTimeout(lt); | |
} | |
if(x!=null){ | |
x.abort(); | |
} | |
x=new XMLHttpRequest(); | |
x.onreadystatechange=function(){ | |
if(x.readyState==4&&x.status==200){ | |
var s=x.responseText; | |
var actualVoltage; | |
if(calibrationStatus == 1){ | |
//Power Calibration | |
if(isInitPower){ | |
isInitPower = false; | |
x.open('GET','cs?c2=&c1=Backlog VoltRes 3; WattRes 3; CurrentSet '+(expectedCurrent*1000+100),true); | |
x.send(); | |
} | |
if(x.responseURL.indexOf("cs") != -1){ | |
var arr = s.split("\n"); | |
var cal = parseFloat(arr[arr.length - 1].replace(/"\{PowerSetCal":([0-9]+)\}/, "$1")); | |
isDelay = true; | |
if(powerSetCal != 0 && averageDiffPower < 0 && cal > powerSetCal){ | |
x.open('GET','cs?c2=&c1=PowerSet '+(Math.round((actualPower+averageDiffPower)*1000)/1000+(0-accuracyPowerRange)),false); | |
}else if(powerSetCal != 0 && averageDiffPower > 0 && cal < powerSetCal){ | |
x.open('GET','cs?c2=&c1=PowerSet '+(Math.round((actualPower+averageDiffPower)*1000)/1000+accuracyPowerRange),false); | |
}else{ | |
powerSetCal = cal; | |
return; | |
} | |
x.send(); | |
return; | |
} | |
//get actual value | |
actualVoltage = parseFloat(s.replace(/.*Voltage{m}(.*)V{e}{s}.*/,"$1").trim()); | |
var actualPower = parseFloat(s.replace(/.*Power{m}(.*)W{e}{s}.*/,"$1").trim()); | |
if(""+actualVoltage+actualPower != prevVal){ | |
prevVal = ""+actualVoltage+actualPower; | |
var calculatedCurrent = actualVoltage/expectedResistance; | |
var calculatedPower = actualVoltage*calculatedCurrent; | |
var diffPower = calculatedPower-actualPower; | |
diffPowerSum += diffPower; | |
diffPowerCnt++; | |
averageDiffPower = diffPowerSum/diffPowerCnt; | |
//Specified number of times | |
if(diffPowerCnt >= samplingsPower){ | |
if(Math.abs(averageDiffPower) <= accuracyPowerRange){ | |
calibrationStatus = 2; | |
}else{ | |
isDelay = true; | |
x.open('GET','cs?c2=&c1=PowerSet '+Math.round((actualPower+averageDiffPower)*1000)/1000,false); | |
x.send(); | |
diffPowerSum = 0; | |
diffPowerCnt = 0; | |
} | |
} | |
} | |
}else if(calibrationStatus == 2){ | |
//Current Calibration | |
if(x.responseURL.indexOf("cs") != -1){ | |
return; | |
} | |
actualVoltage = parseFloat(s.replace(/.*Voltage{m}(.*)V{e}{s}.*/,"$1").trim()); | |
activePower = parseFloat(s.replace(/.*Power{m}(.*)W{e}{s}.*/,"$1").trim()); | |
apparentPower = parseFloat(s.replace(/.*Apparent Power{m}(.*)VA{e}{s}.*/,"$1").trim()); | |
if(isInitCurrent){ | |
isDelay = true; | |
isInitCurrent = false; | |
x.open('GET','cs?c2=&c1=CurrentSet '+Math.round((activePower/actualVoltage)*1000)/1000*1000,false); | |
x.send(); | |
} | |
if(activePower < apparentPower){ | |
activePowerMinCnt++; | |
}else{ | |
apparentPowerMinCnt++; | |
} | |
allCnt = activePowerMinCnt+apparentPowerMinCnt; | |
activePowerRate = activePowerMinCnt / allCnt; | |
apparentPowerRate = apparentPowerMinCnt / allCnt; | |
if(allCnt >= samplingsCurrent){ | |
if(activePowerRate < 0.5-(accuracyCurrentRange/100)){ | |
isDelay = true; | |
x.open('GET','cs?c2=&c1=CurrentSet '+((Math.round((activePower/actualVoltage)*1000)/1000*1000)+1),false); | |
x.send(); | |
}else if(activePowerRate > 0.5+(accuracyCurrentRange/100)){ | |
isDelay = true; | |
x.open('GET','cs?c2=&c1=CurrentSet '+((Math.round((activePower/actualVoltage)*1000)/1000*1000)-1),false); | |
x.send(); | |
}else{ | |
calibrationStatus=3; | |
} | |
activePowerMinCnt = 0; | |
apparentPowerMinCnt = 0; | |
allCnt = 0; | |
} | |
} | |
//Display | |
var status = "</BR>"; | |
var calibration = ""; | |
if(calibrationStatus==1){ | |
status = "</BR><b>Calibration of Power...</b></BR>"; | |
calibration = "Count:"+diffPowerCnt+ | |
"</br>Average diff power:"+(Math.round((averageDiffPower)*1000)/1000) | |
}else if(calibrationStatus==2){ | |
status = "</BR><b>Calibration of Current...</b></BR>"; | |
calibration = "Count:"+allCnt+ | |
"</BR>Actual apparent power:"+(Math.round((apparentPower)*1000)/1000)+ | |
"</BR>Actual active power :"+(Math.round((activePower)*1000)/1000)+ | |
"</BR>active < apparent:"+Math.round(activePowerRate*100)+"%"+ | |
"</BR>active == apparent:"+Math.round(apparentPowerRate*100)+"%" | |
}else if(calibrationStatus==3){ | |
status = "<b>Calibration Done!</b></BR>"; | |
} | |
s = calibration+status+"</BR>"+s | |
s = s.replace(/{t}/g,"<table style='width:100%'") | |
.replace(/{s}/g,"<tr><th>") | |
.replace(/{m}/g,"</th><td>") | |
.replace(/{e}/g,"</td></tr>") | |
.replace(/{c}/g,"%'><div style='text-align:center;font-weight:"); | |
eb('l1').innerHTML=s; | |
} | |
}; | |
x.open('GET','.?m=1'+a,false); | |
x.send(); | |
if(isDelay){ | |
lt=setTimeout(la,3000) | |
isDelay = false; | |
}else{ | |
lt=setTimeout(la,200); | |
} | |
} | |
window.onload=la(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@DDomnick got the same issue with v8.2.0. The script has been running for over 1 hour and still nothing. Increased the samplingsPower by increments of 10, ended with 150 and still not calibrating it. Is the script still working?