diff --git a/src/http/client.ts b/src/http/client.ts index 8c5a838..7816752 100644 --- a/src/http/client.ts +++ b/src/http/client.ts @@ -1,10 +1,11 @@ import { createLogger } from '../logger' -const logger = createLogger('http') +type BodyPromise = Promise +const logger = createLogger('http') let requestTimeout = 0 let lastTimestamp = 0 -const promiseCache = new Map>() +const promiseCache = new Map() const createRequestParams = (): RequestInit => { return { @@ -20,7 +21,11 @@ export const setRequestTimeout = (timeoutMs: number) => { logger.info(`Using ${timeoutMs} millisecond timeout for HTTP requests`) } -export const getDedupedResponse = async (timestamp: number, url: string): Promise => { +const resolveBody = async (request: Request): BodyPromise => { + return (await fetch(request)).text() +} + +export const getDedupedResponseBody = async (timestamp: number, url: string): BodyPromise => { // Clear the cache whenever the timestamp changes if (timestamp !== lastTimestamp) { lastTimestamp = timestamp @@ -35,7 +40,7 @@ export const getDedupedResponse = async (timestamp: number, url: string): Promis const request = new Request(url, createRequestParams()) logger.debug(`GET ${url}`) - const promise = fetch(request) + const promise = resolveBody(request) promiseCache.set(key, promise) return promise diff --git a/src/sensor/iotawatt.ts b/src/sensor/iotawatt.ts index 188b84c..52d55bb 100644 --- a/src/sensor/iotawatt.ts +++ b/src/sensor/iotawatt.ts @@ -9,7 +9,7 @@ import { PowerSensorPollFunction, } from '../sensor' import { Circuit } from '../circuit' -import { getDedupedResponse } from '../http/client' +import { getDedupedResponseBody } from '../http/client' import { Characteristics } from '../characteristics' import { createLogger } from '../logger' @@ -114,10 +114,10 @@ export const getSensorData: PowerSensorPollFunction = async ( const sensor = circuit.sensor as IotawattSensor try { - const configurationResult = (await getDedupedResponse(timestamp, getConfigurationUrl(sensor))).clone() - const configuration = (await configurationResult.json()) as IotawattConfiguration - const statusResult = (await getDedupedResponse(timestamp, getStatusUrl(sensor))).clone() - const status = (await statusResult.json()) as IotawattStatus + const configurationResult = await getDedupedResponseBody(timestamp, getConfigurationUrl(sensor)) + const configuration = JSON.parse(configurationResult) as unknown as IotawattConfiguration + const statusResult = await getDedupedResponseBody(timestamp, getStatusUrl(sensor)) + const status = JSON.parse(statusResult) as unknown as IotawattStatus return { timestamp: timestamp, @@ -142,8 +142,8 @@ export const getCharacteristicsSensorData: CharacteristicsSensorPollFunction = a const sensor = characteristics.sensor as IotawattCharacteristicsSensor try { - const queryResult = (await getDedupedResponse(timestamp, getQueryUrl(sensor))).clone() - const query = (await queryResult.json()) as IotawattCharacteristicsQuery + const queryResult = await getDedupedResponseBody(timestamp, getQueryUrl(sensor)) + const query = JSON.parse(queryResult) as unknown as IotawattCharacteristicsQuery return { timestamp: timestamp, diff --git a/src/sensor/shelly.ts b/src/sensor/shelly.ts index be2172b..a64551f 100644 --- a/src/sensor/shelly.ts +++ b/src/sensor/shelly.ts @@ -10,7 +10,7 @@ import { ShellyType, } from '../sensor' import { Circuit } from '../circuit' -import { getDedupedResponse } from '../http/client' +import { getDedupedResponseBody } from '../http/client' import { Characteristics } from '../characteristics' import { createLogger } from '../logger' @@ -64,10 +64,10 @@ const getSensorDataUrl = (sensor: ShellySensor | ShellyCharacteristicsSensor): s const parseGen1Response = async ( timestamp: number, circuit: Circuit, - httpResponse: Response, + responseBody: string, ): Promise => { const sensor = circuit.sensor as ShellySensor - const data = (await httpResponse.json()) as Gen1StatusResult + const data = JSON.parse(responseBody) as unknown as Gen1StatusResult return { timestamp: timestamp, @@ -79,9 +79,9 @@ const parseGen1Response = async ( const parseGen2PMResponse = async ( timestamp: number, circuit: Circuit, - httpResponse: Response, + responseBody: string, ): Promise => { - const data = (await httpResponse.json()) as Gen2SwitchGetStatusResult + const data = JSON.parse(responseBody) as unknown as Gen2SwitchGetStatusResult return { timestamp: timestamp, @@ -93,10 +93,10 @@ const parseGen2PMResponse = async ( const parseGen2EMResponse = async ( timestamp: number, circuit: Circuit, - httpResponse: Response, + responseBody: string, ): Promise => { const sensor = circuit.sensor as ShellySensor - const data = (await httpResponse.json()) as Gen2EMGetStatusResult + const data = JSON.parse(responseBody) as unknown as Gen2EMGetStatusResult let power = 0 let apparentPower = 0 @@ -136,16 +136,16 @@ export const getSensorData: PowerSensorPollFunction = async ( const url = getSensorDataUrl(sensor) try { - const httpResponse = (await getDedupedResponse(timestamp, url)).clone() + const responseBody = await getDedupedResponseBody(timestamp, url) // Parse the response differently depending on what type of Shelly we're dealing with switch (sensor.shelly.type as ShellyType) { case ShellyType.Gen1: - return await parseGen1Response(timestamp, circuit, httpResponse) + return await parseGen1Response(timestamp, circuit, responseBody) case ShellyType.Gen2PM: - return await parseGen2PMResponse(timestamp, circuit, httpResponse) + return await parseGen2PMResponse(timestamp, circuit, responseBody) case ShellyType.Gen2EM: - return await parseGen2EMResponse(timestamp, circuit, httpResponse) + return await parseGen2EMResponse(timestamp, circuit, responseBody) } } catch (e) { logger.error(e) @@ -166,8 +166,8 @@ export const getCharacteristicsSensorData: CharacteristicsSensorPollFunction = a } try { - const httpResponse = (await getDedupedResponse(timestamp, url)).clone() - const data = (await httpResponse.json()) as Gen2EMGetStatusResult + const result = await getDedupedResponseBody(timestamp, url) + const data = JSON.parse(result) as unknown as Gen2EMGetStatusResult let voltage = 0 let frequency = 0 diff --git a/tests/sensor/shelly.test.ts b/tests/sensor/shelly.test.ts index 9735c18..7d63133 100644 --- a/tests/sensor/shelly.test.ts +++ b/tests/sensor/shelly.test.ts @@ -10,8 +10,8 @@ const gen2pmResponse = fs.readFileSync('./tests/sensor/shelly-plus-1pm.Switch.Ge // Mock getDedupedResponse calls to return real-world data jest.mock('../../src/http/client', () => ({ - getDedupedResponse: async (timestamp: number, url: string) => { - let contents: string | null = null + getDedupedResponseBody: async (timestamp: number, url: string): Promise => { + let contents = '' switch (url) { case 'http://127.0.0.1/status': @@ -25,7 +25,7 @@ jest.mock('../../src/http/client', () => ({ break } - return Promise.resolve(new Response(contents)) + return Promise.resolve(contents) }, })) diff --git a/webif/package-lock.json b/webif/package-lock.json index 2fa21de..16a3423 100644 --- a/webif/package-lock.json +++ b/webif/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.1", "devDependencies": { "@sveltejs/adapter-static": "^3.0.1", - "@sveltejs/kit": "^2.5.0", + "@sveltejs/kit": "^2.9.0", "@sveltejs/vite-plugin-svelte": "^3.0.0", "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.0.0", @@ -658,10 +658,11 @@ } }, "node_modules/@polka/url": { - "version": "1.0.0-next.24", - "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.24.tgz", - "integrity": "sha512-2LuNTFBIO0m7kKIQvvPHN6UE63VjpmL9rnEEaOOaiSPbZK+zUOYIzBAWcED+3XYzhYsd/0mD57VdxAEqqV52CQ==", - "dev": true + "version": "1.0.0-next.28", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.28.tgz", + "integrity": "sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==", + "dev": true, + "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.22.4", @@ -897,23 +898,24 @@ } }, "node_modules/@sveltejs/kit": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.5.0.tgz", - "integrity": "sha512-1uyXvzC2Lu1FZa30T4y5jUAC21R309ZMRG0TPt+PPPbNUoDpy8zSmSNVWYaBWxYDqLGQ5oPNWvjvvF2IjJ1jmA==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.9.0.tgz", + "integrity": "sha512-W3E7ed3ChB6kPqRs2H7tcHp+Z7oiTFC6m+lLyAQQuyXeqw6LdNuuwEUla+5VM0OGgqQD+cYD6+7Xq80vVm17Vg==", "dev": true, "hasInstallScript": true, + "license": "MIT", "dependencies": { "@types/cookie": "^0.6.0", "cookie": "^0.6.0", - "devalue": "^4.3.2", - "esm-env": "^1.0.0", - "import-meta-resolve": "^4.0.0", + "devalue": "^5.1.0", + "esm-env": "^1.2.1", + "import-meta-resolve": "^4.1.0", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "sade": "^1.8.1", "set-cookie-parser": "^2.6.0", - "sirv": "^2.0.4", + "sirv": "^3.0.0", "tiny-glob": "^0.2.9" }, "bin": { @@ -923,9 +925,9 @@ "node": ">=18.13" }, "peerDependencies": { - "@sveltejs/vite-plugin-svelte": "^3.0.0", + "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0", "svelte": "^4.0.0 || ^5.0.0-next.0", - "vite": "^5.0.3" + "vite": "^5.0.3 || ^6.0.0" } }, "node_modules/@sveltejs/vite-plugin-svelte": { @@ -1542,10 +1544,11 @@ } }, "node_modules/devalue": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/devalue/-/devalue-4.3.2.tgz", - "integrity": "sha512-KqFl6pOgOW+Y6wJgu80rHpo2/3H07vr8ntR9rkkFIRETewbf5GaYYcakYfiKz89K+sLsuPkQIZaXDMjUObZwWg==", - "dev": true + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.1.1.tgz", + "integrity": "sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw==", + "dev": true, + "license": "MIT" }, "node_modules/dir-glob": { "version": "3.0.1", @@ -1793,10 +1796,11 @@ } }, "node_modules/esm-env": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.0.0.tgz", - "integrity": "sha512-Cf6VksWPsTuW01vU9Mk/3vRue91Zevka5SjyNf3nEpokFRuqt/KjUQoGAwq9qMmhpLTHmXzSIrFRw8zxWzmFBA==", - "dev": true + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.1.tgz", + "integrity": "sha512-U9JedYYjCnadUlXk7e1Kr+aENQhtUaoaV9+gZm1T8LC/YBAPJx3NSPIAurFOC0U5vrdSevnUJS2/wUVxGwPhng==", + "dev": true, + "license": "MIT" }, "node_modules/espree": { "version": "9.6.1", @@ -2149,10 +2153,11 @@ } }, "node_modules/import-meta-resolve": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.0.0.tgz", - "integrity": "sha512-okYUR7ZQPH+efeuMJGlq4f8ubUgO50kByRPyt/Cy1Io4PSRsPjxME+YlVaCOx+NIToW7hCsZNFJyTPFFKepRSA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz", + "integrity": "sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==", "dev": true, + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -2463,6 +2468,7 @@ "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", "integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" } @@ -2996,17 +3002,18 @@ } }, "node_modules/sirv": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", - "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.0.tgz", + "integrity": "sha512-BPwJGUeDaDCHihkORDchNyyTvWFhcusy1XMmhEVTQTwGeybFbp8YEmB+njbPnth1FibULBSBVwCQni25XlCUDg==", "dev": true, + "license": "MIT", "dependencies": { "@polka/url": "^1.0.0-next.24", "mrmime": "^2.0.0", "totalist": "^3.0.0" }, "engines": { - "node": ">= 10" + "node": ">=18" } }, "node_modules/slash": { @@ -3273,6 +3280,7 @@ "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } diff --git a/webif/package.json b/webif/package.json index 2a80141..1bf1b72 100644 --- a/webif/package.json +++ b/webif/package.json @@ -17,7 +17,7 @@ }, "devDependencies": { "@sveltejs/adapter-static": "^3.0.1", - "@sveltejs/kit": "^2.5.0", + "@sveltejs/kit": "^2.9.0", "@sveltejs/vite-plugin-svelte": "^3.0.0", "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.0.0",