Skip to content

Instantly share code, notes, and snippets.

@kyawswarthwin
Created September 13, 2023 05:06
Show Gist options
  • Save kyawswarthwin/e4bd3d9956a2266ad8481e3c598073c5 to your computer and use it in GitHub Desktop.
Save kyawswarthwin/e4bd3d9956a2266ad8481e3c598073c5 to your computer and use it in GitHub Desktop.
China LiFePO4 Battery QR Code Decoder
function formatError(code, message) {
return {
code,
message,
};
}
function handleResponse(response) {
const formatResponse = ({ ok, status, statusText }, data) => {
const responseData = {
success: ok,
status,
statusText,
data,
};
if (!response.ok) {
return Promise.reject(responseData);
}
return responseData;
};
const contentType = response.headers.get('content-type');
if (contentType?.indexOf('application/json') !== -1) {
return response.json().then((data) => formatResponse(response, data));
}
return response.text().then((data) => formatResponse(response, data));
}
export { formatError, handleResponse };
import { formatError } from './helper.util';
function decode(qrCode) {
if (!qrCode?.length) {
return Promise.reject(formatError(400, 'Bad Request'));
}
qrCode = qrCode.toUpperCase();
const manufacturerCode = qrCode.substring(0, 3);
const productTypeCode = qrCode.substring(3, 4);
const batteryTypeCode = qrCode.substring(4, 5);
const productionDateCode = qrCode.substring(14, 17);
const [manufacturer, manufacturerWebsite] = getManufacturer(manufacturerCode);
const productType = getProductType(productTypeCode);
const [batteryType, nominalVoltage] = getBatteryType(batteryTypeCode);
const [modelNumber, nominalCapacity] = getSpecification(manufacturer, qrCode);
const productionDate = getProductionDate(productionDateCode);
const qrCodeLength = qrCode.length;
const isValidLength =
qrCodeLength === 19 || qrCodeLength === 24 || qrCodeLength === 26;
const isValid =
isValidLength && productType !== 'Unknown' && batteryType !== 'Unknown';
if (!isValid) {
return Promise.reject(formatError(400, 'Invalid QR Code'));
}
const result = {
manufacturer,
manufacturerWebsite: manufacturerWebsite ? manufacturerWebsite : 'Unknown',
productType,
batteryType,
modelNumber,
nominalVoltage: nominalVoltage ? `${nominalVoltage}V` : 'Unknown',
nominalCapacity: nominalCapacity ? `${nominalCapacity}Ah` : 'Unknown',
productionDate,
};
return Promise.resolve(result);
}
function getManufacturer(manufacturerCode) {
const manufacturers = {
'001': ['CATL', 'https://www.catl.com'],
'02Y': ['EVE', 'https://www.evebattery.com'],
'04Q': ['EVE Power', 'https://www.evebattery.com'],
'02K': ['Great Power', 'http://www.greatpower.net'],
'069': ['GREE Altairnano/Yinlong'],
'081': ['REPT', 'https://www.chinarept.com'],
'08I': ['REPT', 'https://www.chinarept.com'],
'08B': ['Lishen', 'http://www.lishen.com.cn'],
'02C': ['Lishen', 'http://www.lishen.com.cn'],
'0AL': ['Ganfeng', 'http://www.ganfenglithium.com'],
'0B5': ['CALB', 'http://www.calb-tech.com'],
OB5: ['CALB', 'http://www.calb-tech.com'],
'09U': ['SVOLT'],
'0F4': ['BYD'],
'07H': ['Higee', 'http://www.ihigee.com'],
'04U': ['Narada Power'],
'00P': ['Sunwoda'],
'0H9': ['Tafel'],
'01Y': ['Narada'],
'03H': ['Gotion', 'https://www.gotion.com.cn'],
};
return manufacturers[manufacturerCode] || ['Unknown'];
}
function getProductType(productTypeCode) {
const productTypes = {
C: 'Battery Cell',
P: 'Battery Pack',
M: 'Battery Module',
};
return productTypes[productTypeCode] || 'Unknown';
}
function getBatteryType(batteryTypeCode) {
const batteryTypes = {
A: ['NiMH', 1.2],
B: ['LiFePO4', 3.2],
C: ['LiMnO2', 3.0],
D: ['LiCoO2', 3.7],
E: ['Ternary', 3.7],
F: ['Supercapacitor'],
G: ['LTO', 2.3],
Z: ['Other'],
};
return batteryTypes[batteryTypeCode] || ['Unknown'];
}
function getSpecification(manufacturer, qrCode) {
let specifications;
let specificationCode = qrCode.substring(5, 7);
if (manufacturer === 'Gotion') {
specifications = {
14: ['52', 52],
'0R': ['52', 52],
16: ['GT102', 102],
'09': ['105', 105],
'1B': ['150', 150],
23: ['GT-340', 340],
'0Y': ['INP2714897A', 50],
};
specificationCode = qrCode.substring(6, 8);
} else if (manufacturer === 'GREE Altairnano/Yinlong') {
specifications = {
A2: ['66160', 40],
A3: ['66160', 45],
A4: ['32140', 9],
};
} else if (manufacturer === 'SVOLT') {
specifications = {
20: ['CL01', 184],
10: ['CE01', 104],
};
} else if (manufacturer === 'BYD') {
specifications = {
A3: ['102', 102],
};
} else if (manufacturer === 'REPT') {
specifications = {
'05': ['205', 205],
O5: ['205', 205],
26: ['CB71173200EA', 280],
29: ['230', 230],
40: ['CB71173204EB', 280],
25: ['135', 135],
54: ['142', 142],
'03': ['CB3914895EA-50A', 50],
'09': ['155', 155],
};
} else {
specifications = {
20: ['202', 202],
'2O': ['202', 202],
21: ['71H3L7', 280],
22: ['173', 173],
24: ['302AH', 302],
'2W': ['71H3L7', 280],
31: ['280', 280],
'3I': ['280', 280],
32: ['125', 125],
52: ['114', 114],
ER: ['234', 234],
43: ['117', 117],
46: ['136', 136],
15: ['50', 50],
64: ['LF90K', 90],
65: ['LF105', 105],
66: ['LF280', 280],
68: ['LF50', 50],
'6C': ['LF100L', 100],
'6D': ['LF100MA', 100],
71: ['LF280N', 280],
'7I': ['LF280N', 280],
72: ['LF230', 230],
73: ['LF304', 304],
75: ['LF173', 173],
76: ['LF280K', 280],
83: ['INP114E', 114],
89: ['INP20P', 20],
E6: ['LF22k', 22],
35: ['4680 sample trial cell', 31],
EJ: ['100', 100],
FE: ['GSP34135214F', 100],
F6: ['GSP27135250F', 100],
FD: ['GSP42173166', 135],
FI: ['50', 50],
'03': ['FE100A', 100],
PB: ['LP71173207', 272],
PE: ['LP54173207', 190],
NA: ['202', 202],
PF: ['LP54173207', 202],
A0: ['48173125', 100],
A3: ['48173125', 100],
AO: ['48173125', 100],
S0: ['50', 50],
SO: ['50', 50],
37: ['L173F230', 230],
C0: ['L160F100B', 100],
CO: ['L160F100B', 100],
'2C': ['L148F88', 88],
G2: ['L194F130A', 130],
BE: ['L300F187', 187],
'3B': ['L173F280', 280],
'2A': ['L148N58A', 58],
'4C': ['L221N169A', 169],
B9: ['l300f177a', 177],
50: ['I50', 50],
G4: ['106', 106],
A1: ['75', 75],
A6: ['142', 142],
C1: ['120Ah', 120],
C5: ['150Ah?', 150],
};
}
return specifications[specificationCode] || ['Unknown'];
}
function getProductionDate(productionDateCode) {
const yearCode = productionDateCode.substring(0, 1);
const monthCode = productionDateCode.substring(1, 2);
const dayCode = productionDateCode.substring(2, 3);
let year;
if (isNaN(yearCode)) {
const code = yearCode.charCodeAt(0);
if (code > 64 && code < 91) {
year = 2020 + (code - 65);
}
} else if (Number(yearCode) > 0) {
year = 2010 + Number(yearCode);
}
let month;
if (isNaN(monthCode)) {
const code = monthCode.charCodeAt(0);
if (code > 64 && code < 68) {
month = 10 + (code - 65);
}
} else if (Number(monthCode) > 0) {
month = Number(monthCode);
}
let day;
if (isNaN(dayCode)) {
const code = dayCode.charCodeAt(0);
if (code > 64 && code < 87) {
day = 10 + (code - 65);
}
} else if (Number(dayCode) > 0) {
day = Number(dayCode);
}
return new Date(year, month && month - 1, day).toLocaleDateString('en-GB', {
year: 'numeric',
month: 'short',
day: 'numeric',
});
}
export default { decode };
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment