Merge branch 'dev'

This commit is contained in:
proddy
2025-12-31 21:26:15 +01:00
parent eaa277fef0
commit 28135c225b
385 changed files with 40221 additions and 38187 deletions

View File

@@ -1,4 +0,0 @@
/.yarn/** linguist-vendored
/.yarn/releases/* binary
/.yarn/plugins/**/* binary
/.pnp.* binary linguist-generated

File diff suppressed because one or more lines are too long

View File

@@ -1,3 +0,0 @@
nodeLinker: node-modules
yarnPath: .yarn/releases/yarn-4.7.0.cjs

View File

@@ -3,10 +3,11 @@ When developing and testing the web interface, it's handy not to bother with re-
# prerequisites
- Install the latest LTS of NodeJS
- Install yarn (`npm install -g yarn`)
- install corepack `npm install -g corepack@latest` and `orepack enable`
- Install pnpm (`corepack use pnpm@latest-10` on linux or `winget install -e --id pnpm.pnpm` on windows). See <https://pnpm.io/installation>
- Install bun (<https://bun.sh/docs/installation>)
- type `yarn` from this `mock-api` folder to build
- type `pnpm install` from this `mock-api` folder to build
# To run
- `yarn standalone` from the main `interface` folder and then navigate to <http://localhost:3000>
- `pnpm standalone` from the main `interface` folder and then navigate to <http://localhost:3000>

View File

@@ -1,33 +1,41 @@
// used to simulate
// - file uploads
// - EventSource (SSE) for log messages
// Mock server for development
// Simulates file uploads and EventSource (SSE) for log messages
import formidable from 'formidable';
function pad(number) {
let r = String(number);
if (r.length === 1) {
r = '0' + r;
}
return r;
}
// Constants reused across requests
const VALID_EXTENSIONS = new Set(['bin', 'json', 'md5']);
const ONE_SECOND_MS = 1000;
const TEN_PERCENT = 10;
// e.g. 2024-03-29 07:02:37.856
Date.prototype.toISOString = function () {
return (
this.getUTCFullYear() +
'-' +
pad(this.getUTCMonth() + 1) +
'-' +
pad(this.getUTCDate()) +
' ' +
pad(this.getUTCHours()) +
':' +
pad(this.getUTCMinutes()) +
':' +
pad(this.getUTCSeconds()) +
'.' +
String((this.getUTCMilliseconds() / 1000).toFixed(3)).slice(2, 5)
// padding function
const pad = (number) => String(number).padStart(2, '0');
// Simple throttle helper (time-based)
const throttle = (fn, intervalMs) => {
let last = 0;
return (...args) => {
const now = Date.now();
if (now - last >= intervalMs) {
last = now;
fn(...args);
}
};
};
// Cached date formatter to avoid prototype pollution
const formatDate = (date) => {
const year = date.getUTCFullYear();
const month = pad(date.getUTCMonth() + 1);
const day = pad(date.getUTCDate());
const hours = pad(date.getUTCHours());
const minutes = pad(date.getUTCMinutes());
const seconds = pad(date.getUTCSeconds());
const milliseconds = String((date.getUTCMilliseconds() / 1000).toFixed(3)).slice(
2,
5
);
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${milliseconds}`;
};
export default () => {
@@ -35,97 +43,174 @@ export default () => {
name: 'vite:mockserver',
configureServer: async (server) => {
server.middlewares.use(async (req, res, next) => {
// catch any file uploads
// Handle file uploads
if (req.url.startsWith('/rest/uploadFile')) {
// show progress
let progress = 0;
const file_size = req.headers['content-length'];
console.log('File size: ' + file_size);
req.on('data', async (chunk) => {
progress += chunk.length;
const percentage = (progress / file_size) * 100;
console.log(`Progress: ${Math.round(percentage)}%`);
// await new Promise((resolve) => setTimeout(() => resolve(), 3000)); // slow it down
});
const form = formidable({});
let fields;
let files;
try {
[fields, files] = await form.parse(req);
} catch (err) {
console.error('Not json form content');
res.writeHead(err.httpCode || 400, {
'Content-Type': 'text/plain'
// CORS preflight support
if (req.method === 'OPTIONS') {
res.writeHead(204, {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Cache-Control',
'Access-Control-Max-Age': '600'
});
res.end(String(err));
res.end();
return;
}
// only process when we have a file
if (Object.keys(files).length > 0) {
const uploaded_file = files.file[0];
const file_name = uploaded_file.originalFilename;
const file_extension = file_name.substring(
file_name.lastIndexOf('.') + 1
if (req.method !== 'POST') {
res.statusCode = 405;
res.setHeader('Allow', 'POST, OPTIONS');
res.end('Method Not Allowed');
return;
}
const fileSize = parseInt(req.headers['content-length'] || '0', 10);
let progress = 0;
// Track upload progress
const logThrottled = throttle((percentage) => {
console.log(`Upload progress: ${percentage}%`);
}, ONE_SECOND_MS);
req.on('data', (chunk) => {
progress += chunk.length;
if (fileSize > 0) {
const percentage = Math.round((progress / fileSize) * 100);
// Only log every ~1s and for meaningful changes (>=10%)
if (percentage % TEN_PERCENT === 0) {
logThrottled(percentage);
}
}
});
try {
const form = formidable({
maxFileSize: 50 * 1024 * 1024, // 50MB limit
keepExtensions: true,
multiples: false,
allowEmptyFiles: false
});
const [fields, files] = await form.parse(req);
if (Object.keys(files).length === 0) {
res.statusCode = 400;
res.end('No file uploaded');
return;
}
const uploadedFile = Array.isArray(files.file)
? files.file[0]
: files.file;
const fileName = uploadedFile.originalFilename;
const fileExtension = fileName
.substring(fileName.lastIndexOf('.') + 1)
.toLowerCase();
console.log(
`File uploaded: ${fileName} (${fileExtension}, ${fileSize} bytes)`
);
console.log('Filename: ' + file_name);
console.log('Extension: ' + file_extension);
console.log('File size: ' + file_size);
// Validate file extension
if (!VALID_EXTENSIONS.has(fileExtension)) {
res.statusCode = 406;
res.end('Invalid file extension');
return;
}
if (file_extension === 'bin' || file_extension === 'json') {
console.log('File uploaded successfully!');
} else if (file_extension === 'md5') {
console.log('MD5 hash generated successfully!');
// Handle different file types
if (fileExtension === 'md5') {
res.setHeader('Content-Type', 'application/json');
res.end(
JSON.stringify({
md5: 'ef4304fc4d9025a58dcf25d71c882d2c'
})
);
} else {
res.statusCode = 406;
console.log('Invalid file extension!');
console.log('File uploaded successfully!');
res.end();
}
} catch (err) {
console.error('Upload error:', err && err.message ? err.message : err);
res.statusCode = err.httpCode || 400;
res.setHeader('Content-Type', 'text/plain');
res.end(err && err.message ? err.message : 'Upload error');
}
res.end();
}
// SSE Eventsource
// Handle Server-Sent Events (SSE) for log streaming
else if (req.url.startsWith('/es/log')) {
// Set SSE headers
res.writeHead(200, {
Connection: 'keep-alive',
'Cache-Control': 'no-cache',
'Content-Type': 'text/event-stream'
'Cache-Control': 'no-cache, no-transform',
'Content-Type': 'text/event-stream',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'Cache-Control',
'X-Accel-Buffering': 'no' // disable proxy buffering (nginx, etc.)
});
let count = 0;
const interval = setInterval(() => {
let message = 'message #' + count;
if (count % 6 === 1) {
// Flush headers early when supported
if (typeof res.flushHeaders === 'function') {
res.flushHeaders();
}
let messageCount = 0;
const logLevels = [3, 4, 5, 6, 7, 8]; // Different log levels
const logNames = ['system', 'ems', 'wifi', 'mqtt', 'ntp', 'api'];
const sendLogMessage = () => {
const level = logLevels[messageCount % logLevels.length];
const name = logNames[messageCount % logNames.length];
let message = `Log message #${messageCount}`;
// Add long message every 6th message
if (messageCount % 6 === 1) {
message +=
' that is a long message that will be wrapped, to test if it gets truncated';
' - This is a longer message to test text wrapping and truncation behavior in the UI';
}
const data = {
t: new Date().toISOString(),
l: 3 + (count % 6),
i: count,
n: 'system',
const logData = {
t: formatDate(new Date()),
l: level,
i: messageCount,
n: name,
m: message
};
count++;
res.write(`data: ${JSON.stringify(data)}\n\n`);
}, 1000);
// if client closes connection
res.on('close', () => {
console.log('Closing ES connection');
clearInterval(interval);
res.end();
});
res.write(`data: ${JSON.stringify(logData)}\n\n`);
messageCount++;
};
// Send initial message
res.write(`retry: 2000\n\n`); // client reconnection delay
sendLogMessage();
// Set up interval for periodic messages
const messageInterval = setInterval(sendLogMessage, 500);
if (typeof messageInterval.unref === 'function') messageInterval.unref();
// Heartbeat to keep connections alive through proxies
const heartbeat = setInterval(() => {
res.write(`:keep-alive ${Date.now()}\n\n`);
}, 15 * ONE_SECOND_MS);
if (typeof heartbeat.unref === 'function') heartbeat.unref();
// Clean up on connection close
const cleanup = () => {
console.log('SSE connection closed');
clearInterval(messageInterval);
clearInterval(heartbeat);
if (!res.destroyed) {
res.end();
}
};
res.on('close', cleanup);
res.on('error', cleanup);
res.on('finish', cleanup);
} else {
next(); // move on to the next middleware function in chain
next(); // Continue to next middleware
}
});
}

View File

@@ -1,19 +1,19 @@
{
"name": "mock-api",
"version": "3.7.2",
"version": "3.8.0",
"description": "mock api for EMS-ESP",
"author": "proddy, emsesp.org",
"license": "MIT",
"scripts": {
"mock-rest": "bun --watch rest_server.ts",
"mock-rest": "bun --watch restServer.ts",
"format": "prettier -l -w '**/*.{ts,tsx,js,css,json,md}'"
},
"dependencies": {
"@msgpack/msgpack": "^3.1.1",
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
"formidable": "^3.5.2",
"itty-router": "^5.0.18",
"prettier": "^3.5.3"
"@msgpack/msgpack": "^3.1.3",
"@trivago/prettier-plugin-sort-imports": "^6.0.0",
"formidable": "^3.5.4",
"itty-router": "^5.0.22",
"prettier": "^3.7.4"
},
"packageManager": "yarn@4.7.0"
"packageManager": "pnpm@10.27.0+sha512.72d699da16b1179c14ba9e64dc71c9a40988cbdc65c264cb0e489db7de917f20dcf4d64d8723625f2969ba52d4b7e2a1170682d9ac2a5dcaeaab732b7e16f04a"
}

View File

@@ -58,6 +58,7 @@ let settings = {
eth_power: 15,
eth_phy_addr: 0,
eth_clock_mode: 1,
led_type: 0,
platform: 'ESP32',
modbus_enabled: false,
modbus_port: 502,
@@ -105,63 +106,93 @@ let system_status = {
psram_size: 8189,
free_psram: 8166,
has_loader: true,
has_partition: true,
partitions: [
{
partition: 'app0', // this one is active
version: 'XX.XX.XX', // defined later
install_date: '2025-03-01T13:29:13.999Z',
size: 4672
},
{
partition: 'app1',
version: '3.8.1-dev.40',
install_date: '2025-03-01T13:29:13.999Z',
size: 4672
},
{
partition: 'factory',
version: '3.8.1-dev.39',
install_date: '2025-03-01T13:29:13.999Z',
size: 4672
}
],
// partitions: [],
developer_mode: true,
model: '',
// model: 'BBQKees Electronics EMS Gateway E32 V2 (E32 V2.0 P3/2024011)',
// status: 0,
status: 3
};
let VERSION_IS_UPGRADEABLE: boolean;
// Versions
// default - on latest stable, no stable upgrades
let THIS_VERSION = '3.7.2';
let LATEST_STABLE_VERSION = '3.7.2';
let LATEST_DEV_VERSION = '3.7.3-dev.9';
// Test Versioning
let DEV_VERSION_IS_UPGRADEABLE: boolean;
let STABLE_VERSION_IS_UPGRADEABLE: boolean;
let THIS_VERSION: string;
let LATEST_STABLE_VERSION = '3.8.0';
let LATEST_DEV_VERSION = '3.8.1-dev.2';
// scenarios for testing versioning
let version_test = 0;
version_test = 0; // on latest stable, no upgrades, but can switch
// version_test = 1; // on latest dev, no update
// version_test = 2; // on stable, upgrade stable to latest stable
// version_test = 3; // on dev, upgrade dev to latest dev
let version_test = 0; // on latest stable, or switch to dev
// let version_test = 1; // on latest dev, or switch back to stable
// let version_test = 2; // upgrade an older stable to latest stable or switch to latest dev
// let version_test = 3; // upgrade dev to latest, or switch to stable
// let version_test = 4; // downgrade to an older dev, or switch back to stable
switch (version_test as number) {
case 0:
default:
// use default - on latest stable, no upgrades, but can switch
VERSION_IS_UPGRADEABLE = false;
// on latest stable, or switch to dev
THIS_VERSION = LATEST_STABLE_VERSION;
STABLE_VERSION_IS_UPGRADEABLE = false;
DEV_VERSION_IS_UPGRADEABLE = true;
break;
case 1:
// on latest dev, no update
THIS_VERSION = '3.7.2-dev.9';
LATEST_STABLE_VERSION = '3.7.2';
LATEST_DEV_VERSION = '3.7.3-dev.9';
VERSION_IS_UPGRADEABLE = false;
// on latest dev, or switch back to stable
THIS_VERSION = LATEST_DEV_VERSION;
STABLE_VERSION_IS_UPGRADEABLE = false;
DEV_VERSION_IS_UPGRADEABLE = false;
break;
case 2:
// upgrade stable to latest stable
THIS_VERSION = '3.6.5';
LATEST_STABLE_VERSION = '3.7.2';
LATEST_DEV_VERSION = '3.7.3-dev.12';
VERSION_IS_UPGRADEABLE = true;
// upgrade an older stable to latest stable or switch to latest dev
THIS_VERSION = '3.8.0';
STABLE_VERSION_IS_UPGRADEABLE = true;
DEV_VERSION_IS_UPGRADEABLE = true;
break;
case 3:
// upgrade dev to latest dev
THIS_VERSION = '3.7.2-dev-1';
LATEST_STABLE_VERSION = '3.7.2';
LATEST_DEV_VERSION = '3.7.3-dev.12';
VERSION_IS_UPGRADEABLE = true;
// upgrade dev to latest, or switch to stable
THIS_VERSION = '3.8.1-dev.3';
STABLE_VERSION_IS_UPGRADEABLE = false;
DEV_VERSION_IS_UPGRADEABLE = true;
break;
case 4:
// downgrade to an older dev, or switch back to stable
THIS_VERSION = '3.8.1-dev.1';
STABLE_VERSION_IS_UPGRADEABLE = true;
DEV_VERSION_IS_UPGRADEABLE = false;
break;
}
// set the version
system_status.emsesp_version = THIS_VERSION;
system_status.partitions[0].version = THIS_VERSION; // app0
const emulate_esp = 'ESP32S3';
// const emulate_esp = 'ESP32';
// set the ESP platform - using ESP32 will disable OTA and automatic version downloading
let emulate_esp: string;
emulate_esp = 'ESP32S3';
// emulate_esp = 'ESP32';
switch (emulate_esp as string) {
switch (emulate_esp) {
// ESP32 4MB
case 'ESP32':
system_status.esp_platform = 'ESP32';
@@ -175,7 +206,7 @@ switch (emulate_esp as string) {
settings.platform = 'ESP32';
break;
// ESP32S3
// ESP32 S3
case 'ESP32S3':
default:
system_status.esp_platform = 'ESP32S3';
@@ -185,7 +216,7 @@ switch (emulate_esp as string) {
system_status.psram = true;
system_status.psram_size = 8189;
system_status.free_psram = 8166;
settings.board_profile = 'S3';
settings.board_profile = 'S32S3';
settings.platform = 'ESP32S3';
break;
}
@@ -269,10 +300,10 @@ function updateMask(entity: any, de: any, dd: any) {
const old_custom_name = dd.nodes[dd_objIndex].cn;
console.log(
'comparing names, old (' +
old_custom_name +
') with new (' +
new_custom_name +
')'
old_custom_name +
') with new (' +
new_custom_name +
')'
);
if (old_custom_name !== new_custom_name) {
changed = true;
@@ -353,7 +384,7 @@ function custom_support() {
'',
"For help and questions please <a target='_blank' href='https://emsesp.org'>contact</a> your installer."
],
img_url: 'https://docs.emsesp.org/_media/images/designer.png'
img_url: 'https://emsesp.org/_media/images/designer.png'
// img_url: 'https://picsum.photos/200/300'
}
};
@@ -365,21 +396,23 @@ function check_upgrade(version: string) {
if (version) {
const dev_version = version.split(',')[0];
const stable_version = version.split(',')[1];
console.log(
'latest dev version: ' +
dev_version +
', latest stable version: ' +
stable_version
);
console.log(
'Version upgrade check from version ' +
THIS_VERSION +
', upgradable: ' +
VERSION_IS_UPGRADEABLE
'Upgrade this version (' +
THIS_VERSION +
') to dev (' +
dev_version +
') is ' +
(DEV_VERSION_IS_UPGRADEABLE ? 'YES' : 'NO') +
' and to stable (' +
stable_version +
') is ' +
(STABLE_VERSION_IS_UPGRADEABLE ? 'YES' : 'NO')
);
data = {
emsesp_version: THIS_VERSION,
upgradeable: VERSION_IS_UPGRADEABLE
dev_upgradeable: DEV_VERSION_IS_UPGRADEABLE,
stable_upgradeable: STABLE_VERSION_IS_UPGRADEABLE
};
} else {
console.log('requesting ems-esp version (' + THIS_VERSION + ')');
@@ -410,8 +443,8 @@ let ntp_settings = {
tz_label: 'Europe/Amsterdam',
tz_format: 'CET-1CEST,M3.5.0,M10.5.0/3'
};
const ntp_status = {
status: 2,
let ntp_status = {
status: 0, // 0 = disabled, 1 = inactive, 2 = active
utc_time: '2021-04-01T14:25:42Z',
local_time: '2021-04-01T16:25:42',
server: 'time.google.com',
@@ -560,14 +593,15 @@ let mqtt_settings = {
publish_time_heartbeat: 60,
publish_time_water: 60,
mqtt_qos: 0,
rootCA: '',
mqtt_retain: false,
ha_enabled: true,
nested_format: 1,
discovery_type: 0,
discovery_prefix: 'homeassistant',
send_response: true,
publish_single: false
publish_single: false,
enableTLS: true,
rootCA: ''
};
const mqtt_status = {
enabled: true,
@@ -953,19 +987,94 @@ const emsesp_coredata_custom = {
const emsesp_sensordata = {
// ts: [],
ts: [
{ id: '28-233D-9497-0C03', n: 'Dallas 1', t: 25.7, o: 1.2, u: 1 },
{ id: '28-243D-7437-1E3A', n: 'Dallas 2 outside', t: 26.1, o: 0, u: 1 },
{ id: '28-243E-7437-1E3B', n: 'Zolder', t: 27.1, o: 0, u: 1 },
{ id: '28-183D-1892-0C33', n: 'Roof', o: 2, u: 1 } // no temperature
{ id: '28-233D-9497-0C03', n: 'Dallas 1', t: 25.7, o: 1.2, u: 1, s: false },
{
id: '28-243D-7437-1E3A',
n: 'Dallas 2 outside',
t: 26.1,
o: 0,
u: 1,
s: false
},
{ id: '28-243E-7437-1E3B', n: 'Zolder', t: 27.1, o: 0, u: 1, s: false },
{ id: '28-183D-1892-0C33', n: 'Roof', o: 2, u: 1, s: false }, // no temperature
{
id: '28_1767_7B13_2502',
n: 'gateway_temperature',
t: 28.1,
o: 0,
u: 1,
s: true
} // internal system temp
],
// as: [],
as: [
{ id: 1, g: 36, n: 'motor', v: 0, u: 0, o: 17, f: 0, t: 0, d: false },
{ id: 2, g: 37, n: 'External switch', v: 13, u: 0, o: 17, f: 0, t: 1, d: false },
{ id: 3, g: 39, n: 'Pulse count', v: 144, u: 0, o: 0, f: 0, t: 2, d: false },
{ id: 4, g: 40, n: 'Pressure', v: 16, u: 17, o: 0, f: 0, t: 3, d: false }
{ id: 1, g: 35, n: 'motor', v: 0, u: 0, o: 17, f: 0, t: 7, d: false, s: false },
{
id: 2,
g: 34,
n: 'External_switch',
v: 13,
u: 0,
o: 17,
f: 0,
t: 1,
d: false,
s: false
},
{
id: 3,
g: 37,
n: 'Pulse_count',
v: 144,
u: 0,
o: 0,
f: 0,
t: 2,
d: false,
s: false
},
{
id: 4,
g: 23,
n: 'Pressure',
v: 16,
u: 17,
o: 0,
f: 0,
t: 3,
d: false,
s: false
},
{
id: 6,
g: 39,
n: 'core_voltage',
v: 3.34,
u: 23,
o: 0,
f: 0.003771,
t: 3,
d: false,
s: true
},
{
id: 7,
g: 36,
n: 'supply_voltage',
v: 12.21,
u: 23,
o: 0,
f: 0.017,
t: 3,
d: false,
s: true
}
],
analog_enabled: true
analog_enabled: true,
available_gpios: [] as number[],
exclude_types: [] as number[],
platform: 'ESP32'
};
const activity = {
@@ -4326,7 +4435,15 @@ router
router
.get(NTP_SETTINGS_ENDPOINT, () => ntp_settings)
.get(NTP_STATUS_ENDPOINT, () => ntp_status)
.post(TIME_ENDPOINT, () => status(200))
.post(TIME_ENDPOINT, async (request: any) => {
const rsp = await request.json();
const local_time = rsp.local_time;
ntp_status.status = 1;
ntp_status.local_time = local_time;
ntp_status.utc_time = local_time;
console.log('ntp time set to', local_time);
return status(200);
})
.post(NTP_SETTINGS_ENDPOINT, async (request: any) => {
ntp_settings = await request.json();
console.log('ntp settings saved', ntp_settings);
@@ -4336,13 +4453,19 @@ router
// SYSTEM and SETTINGS
router
.get(ACTIVITY_ENDPOINT, () => activity)
.get(SYSTEM_STATUS_ENDPOINT, () => {
.get(SYSTEM_STATUS_ENDPOINT, async () => {
if (countHardwarePoll >= 2) {
countHardwarePoll = 0;
system_status.status = 0; // SYSTEM_STATUS_NORMAL
}
countHardwarePoll++;
// Add a small artificial delay to better simulate a real network, to see if flash is fixed
// await new Promise((resolve) => setTimeout(resolve, 3000));
system_status.uptime += 3; // simulate 3 seconds of uptime
system_status.bus_uptime += 3;
return system_status;
})
.get(SECURITY_SETTINGS_ENDPOINT, () => security_settings)
@@ -4359,97 +4482,55 @@ router
// EMS-ESP Project stuff
//
// Lookup maps to avoid repetitive branching per request
const DEVICE_DATA_MAP: Record<number, any> = {
1: emsesp_devicedata_1,
2: emsesp_devicedata_2,
3: emsesp_devicedata_3,
4: emsesp_devicedata_4,
5: emsesp_devicedata_5,
6: emsesp_devicedata_6,
7: emsesp_devicedata_7,
8: emsesp_devicedata_8,
9: emsesp_devicedata_9,
10: emsesp_devicedata_10,
11: emsesp_devicedata_11,
99: emsesp_devicedata_99
};
const DEVICE_ENTITIES_MAP: Record<number, any> = {
1: emsesp_deviceentities_1,
2: emsesp_deviceentities_2,
3: emsesp_deviceentities_3,
4: emsesp_deviceentities_4,
5: emsesp_deviceentities_5,
6: emsesp_deviceentities_6,
7: emsesp_deviceentities_7,
8: emsesp_deviceentities_8,
9: emsesp_deviceentities_9,
10: emsesp_deviceentities_10
};
function deviceData(id: number) {
if (id == 1) {
return new Response(encoder.encode(emsesp_devicedata_1), { headers });
}
if (id == 2) {
return new Response(encoder.encode(emsesp_devicedata_2), { headers });
}
if (id == 3) {
return new Response(encoder.encode(emsesp_devicedata_3), { headers });
}
if (id == 4) {
return new Response(encoder.encode(emsesp_devicedata_4), { headers });
}
if (id == 5) {
return new Response(encoder.encode(emsesp_devicedata_5), { headers });
}
if (id == 6) {
return new Response(encoder.encode(emsesp_devicedata_6), { headers });
}
if (id == 7) {
return new Response(encoder.encode(emsesp_devicedata_7), { headers });
}
if (id == 8) {
if (id === 8) {
// test changing the selected flow temp on a Bosch Compress 7000i AW Heat Pump (Boiler/HP)
emsesp_devicedata_8.nodes[4].v = Math.floor(Math.random() * 100);
return new Response(encoder.encode(emsesp_devicedata_8), { headers });
}
if (id == 9) {
return new Response(encoder.encode(emsesp_devicedata_9), { headers });
}
if (id == 10) {
return new Response(encoder.encode(emsesp_devicedata_10), { headers });
}
if (id == 11) {
return new Response(encoder.encode(emsesp_devicedata_11), { headers });
}
if (id == 99) {
return new Response(encoder.encode(emsesp_devicedata_99), { headers });
const data = DEVICE_DATA_MAP[id];
if (data) {
return new Response(encoder.encode(data) as BodyInit, { headers });
}
}
function deviceEntities(id: number) {
if (id == 1) {
return new Response(encoder.encode(emsesp_deviceentities_1), { headers });
}
if (id == 2) {
return new Response(encoder.encode(emsesp_deviceentities_2), { headers });
}
if (id == 3) {
return new Response(encoder.encode(emsesp_deviceentities_3), { headers });
}
if (id == 4) {
return new Response(encoder.encode(emsesp_deviceentities_4), { headers });
}
if (id == 5) {
return new Response(encoder.encode(emsesp_deviceentities_5), { headers });
}
if (id == 6) {
return new Response(encoder.encode(emsesp_deviceentities_6), { headers });
}
if (id == 7) {
return new Response(encoder.encode(emsesp_deviceentities_7), { headers });
}
if (id == 8) {
return new Response(encoder.encode(emsesp_deviceentities_8), { headers });
}
if (id == 9) {
return new Response(encoder.encode(emsesp_deviceentities_9), { headers });
}
if (id == 10) {
return new Response(encoder.encode(emsesp_deviceentities_10), { headers });
}
// not found, return empty
return new Response(encoder.encode(emsesp_deviceentities_none), { headers });
const data = DEVICE_ENTITIES_MAP[id] || emsesp_deviceentities_none;
return new Response(encoder.encode(data) as BodyInit, { headers });
}
// prepare dashboard data
function getDashboardEntityData(id: number) {
let device_data = {};
if (id == 1) device_data = emsesp_devicedata_1;
else if (id == 2) device_data = emsesp_devicedata_2;
else if (id == 3) device_data = emsesp_devicedata_3;
else if (id == 4) device_data = emsesp_devicedata_4;
else if (id == 5) device_data = emsesp_devicedata_5;
else if (id == 6) device_data = emsesp_devicedata_6;
else if (id == 7) device_data = emsesp_devicedata_7;
else if (id == 8) device_data = emsesp_devicedata_8;
else if (id == 9) device_data = emsesp_devicedata_9;
else if (id == 10) device_data = emsesp_devicedata_10;
else if (id == 11) device_data = emsesp_devicedata_11;
else if (id == 99) device_data = emsesp_devicedata_99;
const device_data = DEVICE_DATA_MAP[id] || { nodes: [] };
// filter device_data on
// - only add favorite (mask has bit 8 set) except for Custom Entities (type 99)
@@ -4471,7 +4552,7 @@ router
settings = await request.json();
console.log('application settings saved', settings);
return status(200); // no restart needed
// return status(205); // restart needed
// return status(205); // reboot required
})
// Device Data
@@ -4486,6 +4567,28 @@ router
.get(EMSESP_SENSOR_DATA_ENDPOINT, () => {
// random change the zolder temperature 0-100
emsesp_sensordata.ts[2].t = Math.floor(Math.random() * 100);
// Build list of available GPIOs (S3 board pins) excluding used ones
// and sort it
const allGPIOs = [
2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18, 21, 33, 34, 35, 36, 37, 38,
45, 46
];
const usedGPIOs = new Set([
settings.led_gpio,
settings.dallas_gpio,
settings.pbutton_gpio,
settings.rx_gpio,
settings.tx_gpio,
...emsesp_sensordata.as.map((item) => item.g)
]);
emsesp_sensordata.available_gpios = allGPIOs
.filter((gpio) => !usedGPIOs.has(gpio))
.sort((a, b) => a - b);
// console.log('available_gpios', emsesp_sensordata.available_gpios);
return emsesp_sensordata;
})
.get(EMSESP_DEVICEDATA_ENDPOINT1, (request) =>
@@ -4507,8 +4610,7 @@ router
{};
let fake = false;
// fake = true; // for testing, shows a subset of data
// let fake = true; // toggle for testing, shows a subset of data
if (!fake) {
// pick EMS devices from coredata
@@ -4539,80 +4641,80 @@ router
}
// add temperature sensor data. no command c
let sensor_data: any[] = [];
sensor_data = emsesp_sensordata.ts.map((item, index) => ({
id: DeviceTypeUniqueID.TEMPERATURESENSOR_UID * 100 + index,
dv: {
id: '00' + item.n,
v: item.t, // value is called t in ts (temperature)
u: item.u
}
}));
dashboard_object = {
id: DeviceTypeUniqueID.TEMPERATURESENSOR_UID,
t: DeviceType.TEMPERATURESENSOR,
nodes: sensor_data
};
// only add to dashboard if we have values
if ((dashboard_object.nodes ?? []).length > 0) {
// remove system sensors first
const enabledTemperatureSensors = emsesp_sensordata.ts.filter(
(item) => !item.s
);
if (enabledTemperatureSensors.length > 0) {
const sensor_data = enabledTemperatureSensors.map((item, index) => ({
id: DeviceTypeUniqueID.TEMPERATURESENSOR_UID * 100 + index,
dv: {
id: '00' + item.n,
v: item.t, // value is called t in ts (temperature)
u: item.u
}
}));
dashboard_object = {
id: DeviceTypeUniqueID.TEMPERATURESENSOR_UID,
t: DeviceType.TEMPERATURESENSOR,
nodes: sensor_data
};
dashboard_nodes.push(dashboard_object);
}
// add analog sensor data. no command c
// remove disabled sensors first (t = 0)
sensor_data = emsesp_sensordata.as.filter((item) => item.t !== 0);
sensor_data = sensor_data.map((item, index) => ({
id: DeviceTypeUniqueID.ANALOGSENSOR_UID * 100 + index,
dv: {
id: '00' + item.n,
v: item.v,
u: item.u
}
}));
dashboard_object = {
id: DeviceTypeUniqueID.ANALOGSENSOR_UID,
t: DeviceType.ANALOGSENSOR,
nodes: sensor_data
};
// only add to dashboard if we have values
if ((dashboard_object.nodes ?? []).length > 0) {
// remove disabled and system sensors first (t = 0) and create data in one pass
const enabledAnalogSensors = emsesp_sensordata.as.filter(
(item) => item.t !== 0 && !item.s
);
if (enabledAnalogSensors.length > 0) {
const sensor_data = enabledAnalogSensors.map((item, index) => ({
id: DeviceTypeUniqueID.ANALOGSENSOR_UID * 100 + index,
dv: {
id: '00' + item.n,
v: item.v,
u: item.u
}
}));
dashboard_object = {
id: DeviceTypeUniqueID.ANALOGSENSOR_UID,
t: DeviceType.ANALOGSENSOR,
nodes: sensor_data
};
dashboard_nodes.push(dashboard_object);
}
// add the scheduler data
// filter emsesp_schedule with only if it has a name
let scheduler_data = emsesp_schedule.schedule.filter((item) => item.name);
let scheduler_data2 = scheduler_data.map((item, index) => ({
id: DeviceTypeUniqueID.SCHEDULER_UID * 100 + index,
dv: {
id: '00' + item.name,
v: item.active ? 'on' : 'off',
c: item.name,
l: ['off', 'on']
}
}));
dashboard_object = {
id: DeviceTypeUniqueID.SCHEDULER_UID,
t: DeviceType.SCHEDULER,
nodes: scheduler_data2
};
// only add to dashboard if we have values
if ((dashboard_object.nodes ?? []).length > 0) {
// filter emsesp_schedule with only if it has a name and create data in one pass
const namedSchedules = emsesp_schedule.schedule.filter((item) => item.name);
if (namedSchedules.length > 0) {
const scheduler_data = namedSchedules.map((item, index) => ({
id: DeviceTypeUniqueID.SCHEDULER_UID * 100 + index,
dv: {
id: '00' + item.name,
v: item.active ? 'on' : 'off',
c: item.name,
l: ['off', 'on']
}
}));
dashboard_object = {
id: DeviceTypeUniqueID.SCHEDULER_UID,
t: DeviceType.SCHEDULER,
nodes: scheduler_data
};
dashboard_nodes.push(dashboard_object);
}
} else {
// for testing only
// add the custom entity data
dashboard_object = {
id: DeviceTypeUniqueID.CUSTOM_UID, // unique ID for custom entities
t: DeviceType.CUSTOM,
nodes: getDashboardEntityData(DeviceTypeUniqueID.CUSTOM_UID)
};
if ((dashboard_object.nodes ?? []).length > 0) {
dashboard_nodes.push(dashboard_object);
}
// dashboard_object = {
// id: DeviceTypeUniqueID.CUSTOM_UID, // unique ID for custom entities
// t: DeviceType.CUSTOM,
// nodes: getDashboardEntityData(DeviceTypeUniqueID.CUSTOM_UID)
// };
// if ((dashboard_object.nodes ?? []).length > 0) {
// dashboard_nodes.push(dashboard_object);
// }
// add the scheduler data
// let scheduler_data = emsesp_schedule.schedule.filter((item) => item.name);
// let scheduler_data2 = scheduler_data.map((item, index) => ({
@@ -4641,8 +4743,12 @@ router
};
// console.log('dashboardData: ', dashboardData);
// Clear references to help with garbage collection
dashboard_nodes = [];
dashboard_object = {};
// return dashboard_data; // if not using msgpack
return new Response(encoder.encode(dashboardData), { headers }); // msgpack it
return new Response(encoder.encode(dashboardData) as BodyInit, { headers }); // msgpack it
})
// Customizations
@@ -4811,6 +4917,7 @@ router
if (objIndex !== -1) {
emsesp_sensordata.ts[objIndex].n = ts.name;
emsesp_sensordata.ts[objIndex].o = ts.offset;
emsesp_sensordata.ts[objIndex].s = ts.is_system;
}
console.log('temp sensor saved', ts);
return status(200);
@@ -4828,21 +4935,25 @@ router
u: as.uom,
t: as.type,
d: as.deleted,
s: as.is_system,
v: 0 // must be added for demo only
});
} else {
if (as.deleted) {
emsesp_sensordata.as[objIndex].d = true;
var filtered = emsesp_sensordata.as.filter(function (value, index, arr) {
return !value.d;
});
emsesp_sensordata.as = filtered;
// Remove deleted items in-place to avoid creating new arrays
for (let i = emsesp_sensordata.as.length - 1; i >= 0; i--) {
if (emsesp_sensordata.as[i].d) {
emsesp_sensordata.as.splice(i, 1);
}
}
} else {
emsesp_sensordata.as[objIndex].n = as.name;
emsesp_sensordata.as[objIndex].f = as.factor;
emsesp_sensordata.as[objIndex].o = as.offset;
emsesp_sensordata.as[objIndex].u = as.uom;
emsesp_sensordata.as[objIndex].t = as.type;
emsesp_sensordata.as[objIndex].s = as.is_system;
}
}
console.log('analog sensor saved', as);
@@ -4864,7 +4975,8 @@ router
phy_type: settings.phy_type,
eth_power: settings.eth_power,
eth_phy_addr: settings.eth_phy_addr,
eth_clock_mode: settings.eth_clock_mode
eth_clock_mode: settings.eth_clock_mode,
led_type: settings.led_type
};
if (board_profile == 'S32') {
@@ -4878,6 +4990,19 @@ router
data.eth_power = 0;
data.eth_phy_addr = 0;
data.eth_clock_mode = 0;
data.led_type = 0;
} else if (board_profile == 'S32S3') {
// BBQKees Gateway S3
data.led_gpio = 2;
data.dallas_gpio = 18;
data.rx_gpio = 5;
data.tx_gpio = 17;
data.pbutton_gpio = 0;
data.phy_type = 0;
data.eth_power = 0;
data.eth_phy_addr = 0;
data.eth_clock_mode = 0;
data.led_type = 0;
} else if (board_profile == 'E32') {
// BBQKees Gateway E32
data.led_gpio = 2;
@@ -4889,6 +5014,19 @@ router
data.eth_power = 16;
data.eth_phy_addr = 1;
data.eth_clock_mode = 0;
data.led_type = 0;
} else if (board_profile == 'E32V2') {
// BBQKees Gateway E32 V2
data.led_gpio = 2;
data.dallas_gpio = 14;
data.rx_gpio = 4;
data.tx_gpio = 5;
data.pbutton_gpio = 34;
data.phy_type = 1;
data.eth_power = 15;
data.eth_phy_addr = 0;
data.eth_clock_mode = 1;
data.led_type = 0;
} else if (board_profile == 'MH-ET') {
// MH-ET Live D1 Mini
data.led_gpio = 2;
@@ -4900,6 +5038,7 @@ router
data.eth_power = 0;
data.eth_phy_addr = 0;
data.eth_clock_mode = 0;
data.led_type = 0;
} else if (board_profile == 'NODEMCU') {
// NodeMCU 32S
data.led_gpio = 2;
@@ -4911,6 +5050,7 @@ router
data.eth_power = 0;
data.eth_phy_addr = 0;
data.eth_clock_mode = 0;
data.led_type = 0;
} else if (board_profile == 'LOLIN') {
// Lolin D32
data.led_gpio = 2;
@@ -4922,6 +5062,7 @@ router
data.eth_power = 0;
data.eth_phy_addr = 0;
data.eth_clock_mode = 0;
data.led_type = 0;
} else if (board_profile == 'OLIMEX') {
// Olimex ESP32-EVB (uses U1TXD/U1RXD/BUTTON, no LED or Dallas)
data.led_gpio = 0;
@@ -4933,6 +5074,7 @@ router
data.eth_power = -1;
data.eth_phy_addr = 0;
data.eth_clock_mode = 0;
data.led_type = 0;
} else if (board_profile == 'OLIMEXPOE') {
// Olimex ESP32-POE
data.led_gpio = 0;
@@ -4944,6 +5086,7 @@ router
data.eth_power = 12;
data.eth_phy_addr = 0;
data.eth_clock_mode = 3;
data.led_type = 0;
} else if (board_profile == 'C3MINI') {
// Lolin C3 mini
data.led_gpio = 7;
@@ -4955,6 +5098,7 @@ router
data.eth_power = 0;
data.eth_phy_addr = 0;
data.eth_clock_mode = 0;
data.led_type = 0;
} else if (board_profile == 'S2MINI') {
// Lolin C3 mini
data.led_gpio = 15;
@@ -4966,6 +5110,7 @@ router
data.eth_power = 0;
data.eth_phy_addr = 0;
data.eth_clock_mode = 0;
data.led_type = 0;
} else if (board_profile == 'S3MINI') {
// Liligo S3 mini
data.led_gpio = 17;
@@ -4977,6 +5122,7 @@ router
data.eth_power = 0;
data.eth_phy_addr = 0;
data.eth_clock_mode = 0;
data.led_type = 0;
}
data.board_profile =
@@ -5009,6 +5155,14 @@ router
// upload URL
console.log('upload File from URL', content.param);
return status(200);
} else if (action === 'resetMQTT') {
// reset MQTT
console.log('resetting MQTT...');
return status(200);
} else if (action === 'setPartition') {
// set partition
console.log('setting partition to', content.param);
return status(200);
}
}
return status(404); // cmd not found

View File

@@ -1,308 +0,0 @@
# This file is generated by running "yarn install" inside your project.
# Manual changes might be lost - proceed with caution!
__metadata:
version: 8
cacheKey: 10c0
"@babel/code-frame@npm:^7.26.2":
version: 7.26.2
resolution: "@babel/code-frame@npm:7.26.2"
dependencies:
"@babel/helper-validator-identifier": "npm:^7.25.9"
js-tokens: "npm:^4.0.0"
picocolors: "npm:^1.0.0"
checksum: 10c0/7d79621a6849183c415486af99b1a20b84737e8c11cd55b6544f688c51ce1fd710e6d869c3dd21232023da272a79b91efb3e83b5bc2dc65c1187c5fcd1b72ea8
languageName: node
linkType: hard
"@babel/generator@npm:^7.26.10, @babel/generator@npm:^7.26.5":
version: 7.26.10
resolution: "@babel/generator@npm:7.26.10"
dependencies:
"@babel/parser": "npm:^7.26.10"
"@babel/types": "npm:^7.26.10"
"@jridgewell/gen-mapping": "npm:^0.3.5"
"@jridgewell/trace-mapping": "npm:^0.3.25"
jsesc: "npm:^3.0.2"
checksum: 10c0/88b3b3ea80592fc89349c4e1a145e1386e4042866d2507298adf452bf972f68d13bf699a845e6ab8c028bd52c2247013eb1221b86e1db5c9779faacba9c4b10e
languageName: node
linkType: hard
"@babel/helper-string-parser@npm:^7.25.9":
version: 7.25.9
resolution: "@babel/helper-string-parser@npm:7.25.9"
checksum: 10c0/7244b45d8e65f6b4338a6a68a8556f2cb161b782343e97281a5f2b9b93e420cad0d9f5773a59d79f61d0c448913d06f6a2358a87f2e203cf112e3c5b53522ee6
languageName: node
linkType: hard
"@babel/helper-validator-identifier@npm:^7.25.9":
version: 7.25.9
resolution: "@babel/helper-validator-identifier@npm:7.25.9"
checksum: 10c0/4fc6f830177b7b7e887ad3277ddb3b91d81e6c4a24151540d9d1023e8dc6b1c0505f0f0628ae653601eb4388a8db45c1c14b2c07a9173837aef7e4116456259d
languageName: node
linkType: hard
"@babel/parser@npm:^7.26.10, @babel/parser@npm:^7.26.7, @babel/parser@npm:^7.26.9":
version: 7.26.10
resolution: "@babel/parser@npm:7.26.10"
dependencies:
"@babel/types": "npm:^7.26.10"
bin:
parser: ./bin/babel-parser.js
checksum: 10c0/c47f5c0f63cd12a663e9dc94a635f9efbb5059d98086a92286d7764357c66bceba18ccbe79333e01e9be3bfb8caba34b3aaebfd8e62c3d5921c8cf907267be75
languageName: node
linkType: hard
"@babel/template@npm:^7.26.9":
version: 7.26.9
resolution: "@babel/template@npm:7.26.9"
dependencies:
"@babel/code-frame": "npm:^7.26.2"
"@babel/parser": "npm:^7.26.9"
"@babel/types": "npm:^7.26.9"
checksum: 10c0/019b1c4129cc01ad63e17529089c2c559c74709d225f595eee017af227fee11ae8a97a6ab19ae6768b8aa22d8d75dcb60a00b28f52e9fa78140672d928bc1ae9
languageName: node
linkType: hard
"@babel/traverse@npm:^7.26.7":
version: 7.26.10
resolution: "@babel/traverse@npm:7.26.10"
dependencies:
"@babel/code-frame": "npm:^7.26.2"
"@babel/generator": "npm:^7.26.10"
"@babel/parser": "npm:^7.26.10"
"@babel/template": "npm:^7.26.9"
"@babel/types": "npm:^7.26.10"
debug: "npm:^4.3.1"
globals: "npm:^11.1.0"
checksum: 10c0/4e86bb4e3c30a6162bb91df86329df79d96566c3e2d9ccba04f108c30473a3a4fd360d9990531493d90f6a12004f10f616bf9b9229ca30c816b708615e9de2ac
languageName: node
linkType: hard
"@babel/types@npm:^7.26.10, @babel/types@npm:^7.26.7, @babel/types@npm:^7.26.9":
version: 7.26.10
resolution: "@babel/types@npm:7.26.10"
dependencies:
"@babel/helper-string-parser": "npm:^7.25.9"
"@babel/helper-validator-identifier": "npm:^7.25.9"
checksum: 10c0/7a7f83f568bfc3dfabfaf9ae3a97ab5c061726c0afa7dcd94226d4f84a81559da368ed79671e3a8039d16f12476cf110381a377ebdea07587925f69628200dac
languageName: node
linkType: hard
"@jridgewell/gen-mapping@npm:^0.3.5":
version: 0.3.8
resolution: "@jridgewell/gen-mapping@npm:0.3.8"
dependencies:
"@jridgewell/set-array": "npm:^1.2.1"
"@jridgewell/sourcemap-codec": "npm:^1.4.10"
"@jridgewell/trace-mapping": "npm:^0.3.24"
checksum: 10c0/c668feaf86c501d7c804904a61c23c67447b2137b813b9ce03eca82cb9d65ac7006d766c218685d76e3d72828279b6ee26c347aa1119dab23fbaf36aed51585a
languageName: node
linkType: hard
"@jridgewell/resolve-uri@npm:^3.1.0":
version: 3.1.2
resolution: "@jridgewell/resolve-uri@npm:3.1.2"
checksum: 10c0/d502e6fb516b35032331406d4e962c21fe77cdf1cbdb49c6142bcbd9e30507094b18972778a6e27cbad756209cfe34b1a27729e6fa08a2eb92b33943f680cf1e
languageName: node
linkType: hard
"@jridgewell/set-array@npm:^1.2.1":
version: 1.2.1
resolution: "@jridgewell/set-array@npm:1.2.1"
checksum: 10c0/2a5aa7b4b5c3464c895c802d8ae3f3d2b92fcbe84ad12f8d0bfbb1f5ad006717e7577ee1fd2eac00c088abe486c7adb27976f45d2941ff6b0b92b2c3302c60f4
languageName: node
linkType: hard
"@jridgewell/sourcemap-codec@npm:^1.4.10, @jridgewell/sourcemap-codec@npm:^1.4.14":
version: 1.5.0
resolution: "@jridgewell/sourcemap-codec@npm:1.5.0"
checksum: 10c0/2eb864f276eb1096c3c11da3e9bb518f6d9fc0023c78344cdc037abadc725172c70314bdb360f2d4b7bffec7f5d657ce006816bc5d4ecb35e61b66132db00c18
languageName: node
linkType: hard
"@jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.25":
version: 0.3.25
resolution: "@jridgewell/trace-mapping@npm:0.3.25"
dependencies:
"@jridgewell/resolve-uri": "npm:^3.1.0"
"@jridgewell/sourcemap-codec": "npm:^1.4.14"
checksum: 10c0/3d1ce6ebc69df9682a5a8896b414c6537e428a1d68b02fcc8363b04284a8ca0df04d0ee3013132252ab14f2527bc13bea6526a912ecb5658f0e39fd2860b4df4
languageName: node
linkType: hard
"@msgpack/msgpack@npm:^3.1.1":
version: 3.1.1
resolution: "@msgpack/msgpack@npm:3.1.1"
checksum: 10c0/f7048d2145ee39e4979b4e6bb9d9723db031ea7b4f00d0ec96258fbff40b8af027783f391ee294e5385a5a91849a77d934ad08eb11d08253484eee8f8f0866ff
languageName: node
linkType: hard
"@trivago/prettier-plugin-sort-imports@npm:^5.2.2":
version: 5.2.2
resolution: "@trivago/prettier-plugin-sort-imports@npm:5.2.2"
dependencies:
"@babel/generator": "npm:^7.26.5"
"@babel/parser": "npm:^7.26.7"
"@babel/traverse": "npm:^7.26.7"
"@babel/types": "npm:^7.26.7"
javascript-natural-sort: "npm:^0.7.1"
lodash: "npm:^4.17.21"
peerDependencies:
"@vue/compiler-sfc": 3.x
prettier: 2.x - 3.x
prettier-plugin-svelte: 3.x
svelte: 4.x || 5.x
peerDependenciesMeta:
"@vue/compiler-sfc":
optional: true
prettier-plugin-svelte:
optional: true
svelte:
optional: true
checksum: 10c0/2a4f0464f1f5a294bcd34558fb053f8263f0c62c4a7fcdd3ce40c9822a68ac8b4d951700ab6d01eb3919efe0ed44e4191997edd494d59679b22db1c0db00474e
languageName: node
linkType: hard
"asap@npm:^2.0.0":
version: 2.0.6
resolution: "asap@npm:2.0.6"
checksum: 10c0/c6d5e39fe1f15e4b87677460bd66b66050cd14c772269cee6688824c1410a08ab20254bb6784f9afb75af9144a9f9a7692d49547f4d19d715aeb7c0318f3136d
languageName: node
linkType: hard
"debug@npm:^4.3.1":
version: 4.4.0
resolution: "debug@npm:4.4.0"
dependencies:
ms: "npm:^2.1.3"
peerDependenciesMeta:
supports-color:
optional: true
checksum: 10c0/db94f1a182bf886f57b4755f85b3a74c39b5114b9377b7ab375dc2cfa3454f09490cc6c30f829df3fc8042bc8b8995f6567ce5cd96f3bc3688bd24027197d9de
languageName: node
linkType: hard
"dezalgo@npm:^1.0.4":
version: 1.0.4
resolution: "dezalgo@npm:1.0.4"
dependencies:
asap: "npm:^2.0.0"
wrappy: "npm:1"
checksum: 10c0/8a870ed42eade9a397e6141fe5c025148a59ed52f1f28b1db5de216b4d57f0af7a257070c3af7ce3d5508c1ce9dd5009028a76f4b2cc9370dc56551d2355fad8
languageName: node
linkType: hard
"formidable@npm:^3.5.2":
version: 3.5.2
resolution: "formidable@npm:3.5.2"
dependencies:
dezalgo: "npm:^1.0.4"
hexoid: "npm:^2.0.0"
once: "npm:^1.4.0"
checksum: 10c0/c26d89ba84d392f0e68ba1aca9f779e0f2e94db053d95df562c730782956f302e3f069c07ab96f991415af915ac7b8771f4c813d298df43577fdf439e1e8741e
languageName: node
linkType: hard
"globals@npm:^11.1.0":
version: 11.12.0
resolution: "globals@npm:11.12.0"
checksum: 10c0/758f9f258e7b19226bd8d4af5d3b0dcf7038780fb23d82e6f98932c44e239f884847f1766e8fa9cc5635ccb3204f7fa7314d4408dd4002a5e8ea827b4018f0a1
languageName: node
linkType: hard
"hexoid@npm:^2.0.0":
version: 2.0.0
resolution: "hexoid@npm:2.0.0"
checksum: 10c0/a9d5e6f4adeaefcb4a53803dd48bf0a242d92e8ec699555aea616c4bf8f91788f03093595085976f63d6830815dd080c063503540fabc7e854ebfb11161687c6
languageName: node
linkType: hard
"itty-router@npm:^5.0.18":
version: 5.0.18
resolution: "itty-router@npm:5.0.18"
checksum: 10c0/f21afcf16135622de665b340e16262add2c35dab037c051448df8a2dd49bd83dfa1cf9323000839994157202befaa79f95bbc9e0ed106114cb9da66275ad9408
languageName: node
linkType: hard
"javascript-natural-sort@npm:^0.7.1":
version: 0.7.1
resolution: "javascript-natural-sort@npm:0.7.1"
checksum: 10c0/340f8ffc5d30fb516e06dc540e8fa9e0b93c865cf49d791fed3eac3bdc5fc71f0066fc81d44ec1433edc87caecaf9f13eec4a1fce8c5beafc709a71eaedae6fe
languageName: node
linkType: hard
"js-tokens@npm:^4.0.0":
version: 4.0.0
resolution: "js-tokens@npm:4.0.0"
checksum: 10c0/e248708d377aa058eacf2037b07ded847790e6de892bbad3dac0abba2e759cb9f121b00099a65195616badcb6eca8d14d975cb3e89eb1cfda644756402c8aeed
languageName: node
linkType: hard
"jsesc@npm:^3.0.2":
version: 3.1.0
resolution: "jsesc@npm:3.1.0"
bin:
jsesc: bin/jsesc
checksum: 10c0/531779df5ec94f47e462da26b4cbf05eb88a83d9f08aac2ba04206508fc598527a153d08bd462bae82fc78b3eaa1a908e1a4a79f886e9238641c4cdefaf118b1
languageName: node
linkType: hard
"lodash@npm:^4.17.21":
version: 4.17.21
resolution: "lodash@npm:4.17.21"
checksum: 10c0/d8cbea072bb08655bb4c989da418994b073a608dffa608b09ac04b43a791b12aeae7cd7ad919aa4c925f33b48490b5cfe6c1f71d827956071dae2e7bb3a6b74c
languageName: node
linkType: hard
"mock-api@workspace:.":
version: 0.0.0-use.local
resolution: "mock-api@workspace:."
dependencies:
"@msgpack/msgpack": "npm:^3.1.1"
"@trivago/prettier-plugin-sort-imports": "npm:^5.2.2"
formidable: "npm:^3.5.2"
itty-router: "npm:^5.0.18"
prettier: "npm:^3.5.3"
languageName: unknown
linkType: soft
"ms@npm:^2.1.3":
version: 2.1.3
resolution: "ms@npm:2.1.3"
checksum: 10c0/d924b57e7312b3b63ad21fc5b3dc0af5e78d61a1fc7cfb5457edaf26326bf62be5307cc87ffb6862ef1c2b33b0233cdb5d4f01c4c958cc0d660948b65a287a48
languageName: node
linkType: hard
"once@npm:^1.4.0":
version: 1.4.0
resolution: "once@npm:1.4.0"
dependencies:
wrappy: "npm:1"
checksum: 10c0/5d48aca287dfefabd756621c5dfce5c91a549a93e9fdb7b8246bc4c4790aa2ec17b34a260530474635147aeb631a2dcc8b32c613df0675f96041cbb8244517d0
languageName: node
linkType: hard
"picocolors@npm:^1.0.0":
version: 1.1.1
resolution: "picocolors@npm:1.1.1"
checksum: 10c0/e2e3e8170ab9d7c7421969adaa7e1b31434f789afb9b3f115f6b96d91945041ac3ceb02e9ec6fe6510ff036bcc0bf91e69a1772edc0b707e12b19c0f2d6bcf58
languageName: node
linkType: hard
"prettier@npm:^3.5.3":
version: 3.5.3
resolution: "prettier@npm:3.5.3"
bin:
prettier: bin/prettier.cjs
checksum: 10c0/3880cb90b9dc0635819ab52ff571518c35bd7f15a6e80a2054c05dbc8a3aa6e74f135519e91197de63705bcb38388ded7e7230e2178432a1468005406238b877
languageName: node
linkType: hard
"wrappy@npm:1":
version: 1.0.2
resolution: "wrappy@npm:1.0.2"
checksum: 10c0/56fece1a4018c6a6c8e28fbc88c87e0fbf4ea8fd64fc6c63b18f4acc4bd13e0ad2515189786dd2c30d3eec9663d70f4ecf699330002f8ccb547e4a18231fc9f0
languageName: node
linkType: hard