Skip to content

Commit d5330be

Browse files
committed
feat: cache runtime camera stream URL in FiveMClient
Add cameraStreamUrl property to FiveMClient that is populated from the printer-reported CameraStreamUrl field in machine-info responses, enabling OEM camera auto-detection without manual configuration. - Add FiveMClient.cameraStreamUrl property (cleared on dispose) - Populate from info.CameraStreamUrl in updateMachineInfo() - Add test coverage in FiveMClient.test.ts and MachineInfo.test.ts - Bump version to 1.2.0
1 parent b6e38ed commit d5330be

5 files changed

Lines changed: 49 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [1.2.0] - 2026-03-08
11+
12+
### Added
13+
14+
- `FiveMClient.cameraStreamUrl` — caches the OEM camera stream URL reported by the printer in machine-info responses, cleared on dispose
15+
16+
### Changed
17+
18+
- `FiveMClient.updateMachineInfo()` now populates `cameraStreamUrl` from `info.CameraStreamUrl`
19+
1020
## [1.1.0] - 2026-03-08
1121

1222
### Added

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@ghosttypes/ff-api",
3-
"version": "1.1.0",
3+
"version": "1.2.0",
44
"description": "FlashForge 3D Printer API for Node.js",
55
"main": "dist/index.js",
66
"types": "dist/index.d.ts",

src/FiveMClient.test.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import { beforeEach, describe, expect, it, vi } from 'vitest';
66
import { FiveMClient } from './FiveMClient';
7+
import type { FFMachineInfo } from './models/ff-models';
78

89
const { flashForgeClientConstructor, axiosCreate } = vi.hoisted(() => ({
910
flashForgeClientConstructor: vi.fn(),
@@ -51,4 +52,23 @@ describe('FiveMClient', () => {
5152
expect(client.getEndpoint('/detail')).toBe('http://192.168.1.10:19098/detail');
5253
expect(flashForgeClientConstructor).toHaveBeenCalledWith('192.168.1.10', { port: 19099 });
5354
});
55+
56+
it('caches the runtime OEM camera stream URL from machine info', () => {
57+
const client = new FiveMClient('192.168.1.10', 'SN-1', 'CHK-1');
58+
const machineInfo = {
59+
Name: 'FlashForge 5M',
60+
IsPro: false,
61+
IsAD5X: false,
62+
FirmwareVersion: '3.2.7',
63+
CameraStreamUrl: 'http://192.168.1.10:8080/?action=stream',
64+
MacAddress: 'AA:BB:CC:DD:EE:FF',
65+
FlashCloudRegisterCode: '',
66+
PolarCloudRegisterCode: '',
67+
FormattedTotalRunTime: '0h:0m',
68+
CumulativeFilament: 0,
69+
} as FFMachineInfo;
70+
71+
expect(client.cacheDetails(machineInfo)).toBe(true);
72+
expect(client.cameraStreamUrl).toBe('http://192.168.1.10:8080/?action=stream');
73+
});
5474
});

src/FiveMClient.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ export class FiveMClient {
5959
public isAD5X: boolean = false;
6060
public firmwareVersion: string = '';
6161
public firmVer: string = '';
62+
public cameraStreamUrl: string = '';
6263

6364
public ipAddress: string;
6465
public macAddress: string = '';
@@ -161,6 +162,7 @@ export class FiveMClient {
161162
* Disposes of the FiveMClient instance, stopping keep-alive messages and cleaning up resources.
162163
*/
163164
public async dispose(): Promise<void> {
165+
this.cameraStreamUrl = '';
164166
await this.tcpClient.dispose();
165167
}
166168

@@ -178,6 +180,7 @@ export class FiveMClient {
178180
this.isAD5X = info.IsAD5X; // Cache the AD5X status
179181
this.firmwareVersion = info.FirmwareVersion || '';
180182
this.firmVer = info.FirmwareVersion ? info.FirmwareVersion.split('-')[0] : '';
183+
this.cameraStreamUrl = info.CameraStreamUrl || '';
181184
this.macAddress = info.MacAddress || '';
182185
this.flashCloudCode = info.FlashCloudRegisterCode || '';
183186
this.polarCloudCode = info.PolarCloudRegisterCode || '';

src/models/MachineInfo.test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ describe('MachineInfo', () => {
127127
expect(result.IsAD5X).toBe(true);
128128
expect(result.IsPro).toBe(false); // As per our logic Name=AD5X implies IsPro=false
129129
expect(result.FirmwareVersion).toBe('1.1.3-1.0.8');
130+
expect(result.CameraStreamUrl).toBe('');
130131

131132
expect(result.HasMatlStation).toBe(true);
132133
expect(result.CoolingFanLeftSpeed).toBe(0);
@@ -185,6 +186,7 @@ describe('MachineInfo', () => {
185186
expect(result.IsAD5X).toBe(false);
186187
expect(result.IsPro).toBe(false); // "FlashForge 5M" does not contain "Pro"
187188
expect(result.FirmwareVersion).toBe('1.0.0');
189+
expect(result.CameraStreamUrl).toBe('');
188190

189191
expect(result.HasMatlStation).toBeUndefined();
190192
expect(result.MatlStationInfo).toBeUndefined();
@@ -230,10 +232,23 @@ describe('MachineInfo', () => {
230232
expect(result.IsAD5X).toBe(false);
231233
expect(result.IsPro).toBe(false);
232234
expect(result.FirmwareVersion).toBe(''); // Defaults to empty string
235+
expect(result.CameraStreamUrl).toBe('');
233236
expect(result.CoolingFanSpeed).toBe(0); // Defaults to 0
234237
expect(result.PrintBed.current).toBe(0);
235238
expect(result.Extruder.set).toBe(0);
236239
expect(result.MachineState).toBe(MachineState.Unknown); // status is empty
237240
});
241+
242+
it('preserves a populated camera stream URL from detail data', () => {
243+
const result = machineInfoConverter.fromDetail({
244+
...GENERIC_PRINTER_DETAIL_JSON,
245+
cameraStreamUrl: 'http://192.168.1.100:8080/?action=stream',
246+
});
247+
248+
expect(result).not.toBeNull();
249+
if (!result) return;
250+
251+
expect(result.CameraStreamUrl).toBe('http://192.168.1.100:8080/?action=stream');
252+
});
238253
});
239254
});

0 commit comments

Comments
 (0)