Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ Raw API responses (`FFPrinterDetail` in `src/models/ff-models.ts`) are transform

- `NetworkUtils` (`src/api/network/NetworkUtils.ts`) — Response validation helpers; checks `GenericResponse.code` for success.
- `FNetCode` (`src/api/network/FNetCode.ts`) — Network code constants.
- `FlashForgePrinterDiscovery` (`src/api/PrinterDiscovery.ts`) — UDP broadcast discovery on port 48899, parses binary response buffers at fixed offsets for printer name and serial number.
- `PrinterDiscovery` (`src/api/PrinterDiscovery.ts`) — Universal UDP multicast/broadcast discovery supporting all FlashForge models (AD5X, 5M, 5M Pro, Adventurer 4, Adventurer 3) with multi-protocol response parsing (276-byte modern, 140-byte legacy).

### TCP Response Parsers

Expand Down
285 changes: 285 additions & 0 deletions docs/MIGRATION_GUIDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
# Printer Discovery API Migration Guide

**Version:** 2.0.0
**Date:** 2025-02-08
**Breaking Changes:** Yes

## Overview

The printer discovery API has been completely rewritten to support all FlashForge printer models (AD5X, 5M, 5M Pro, Adventurer 4, Adventurer 3) with multi-protocol UDP discovery. The legacy API has been removed.

## What Changed

| Old API | New API |
|---------|---------|
| `FlashForgePrinterDiscovery` | `PrinterDiscovery` |
| `FlashForgePrinter` | `DiscoveredPrinter` (interface) |
| `discoverPrintersAsync(timeout, idleTimeout, maxRetries)` | `discover({ timeout, idleTimeout, maxRetries })` |
| `isAD5X?: boolean` | `model: PrinterModel` |
| Limited metadata (name, serial, IP) | Complete metadata (model, ports, status, etc.) |

## Migration Examples

### Basic Discovery

**Before (v1.x):**
```typescript
import { FlashForgePrinterDiscovery, FlashForgePrinter } from '@ghosttypes/ff-api';

const discovery = new FlashForgePrinterDiscovery();
const printers: FlashForgePrinter[] = await discovery.discoverPrintersAsync(10000, 1500, 3);

printers.forEach(printer => {
console.log(`${printer.name} - ${printer.ipAddress}`);
if (printer.isAD5X) {
console.log(' AD5X detected');
}
});
```

**After (v2.x):**
```typescript
import { PrinterDiscovery, PrinterModel, type DiscoveredPrinter } from '@ghosttypes/ff-api';

const discovery = new PrinterDiscovery();
const printers: DiscoveredPrinter[] = await discovery.discover({
timeout: 10000,
idleTimeout: 1500,
maxRetries: 3
});

printers.forEach(printer => {
console.log(`${printer.model}: ${printer.name} - ${printer.ipAddress}`);
if (printer.model === PrinterModel.AD5X) {
console.log(' AD5X detected');
}
});
```

### Model Detection

**Before:**
```typescript
if (printer.isAD5X) {
// Handle AD5X
} else {
// Handle other models
}
```

**After:**
```typescript
switch (printer.model) {
case PrinterModel.AD5X:
// Handle AD5X
break;
case PrinterModel.Adventurer5MPro:
// Handle 5M Pro
break;
case PrinterModel.Adventurer5M:
// Handle 5M
break;
case PrinterModel.Adventurer4:
// Handle Adventurer 4
break;
case PrinterModel.Adventurer3:
// Handle Adventurer 3
break;
default:
// Unknown model
break;
}
```

### Accessing Additional Properties

**Before:**
```typescript
const printer: FlashForgePrinter = {
name: 'AD5X',
serialNumber: 'SN123',
ipAddress: '192.168.1.100',
isAD5X: true
};
```

**After:**
```typescript
const printer: DiscoveredPrinter = {
model: PrinterModel.AD5X,
protocolFormat: DiscoveryProtocol.Modern,
name: 'AD5X',
ipAddress: '192.168.1.100',
commandPort: 8899,
serialNumber: 'SN123',
eventPort: 8898,
vendorId: 0x2B71,
productId: 0x0024,
productType: 0x5A02,
statusCode: 0,
status: PrinterStatus.Ready
};
```

### Custom Discovery Options

**Before:**
```typescript
await discovery.discoverPrintersAsync(5000, 1000, 1);
```

**After:**
```typescript
await discovery.discover({
timeout: 5000,
idleTimeout: 1000,
maxRetries: 1,
useMulticast: true,
useBroadcast: true,
ports: [19000, 8899]
});
```

## New Features

### Event-Based Monitoring

The new API supports continuous monitoring with events:

```typescript
const discovery = new PrinterDiscovery();
const monitor = discovery.monitor({ timeout: 30000 });

monitor.on('discovered', (printer: DiscoveredPrinter) => {
console.log(`✓ Found: ${printer.model} - ${printer.name}`);
});

monitor.on('end', () => {
console.log('Discovery complete');
});

monitor.on('error', (error: Error) => {
console.error('Discovery error:', error);
});
```

### Printer Status

The new API includes printer status from discovery:

```typescript
const printer: DiscoveredPrinter = await discovery.discover();
Comment thread
GhostTypes marked this conversation as resolved.
Outdated

if (printer.status === PrinterStatus.Ready) {
console.log('Printer is ready to print');
} else if (printer.status === PrinterStatus.Busy) {
console.log('Printer is busy');
} else if (printer.status === PrinterStatus.Error) {
console.log('Printer has an error');
}
```

### Multi-Protocol Support

The new API automatically handles multiple protocols:

- **Modern Protocol (276-byte)**: AD5X, 5M, 5M Pro
- **Legacy Protocol (140-byte)**: Adventurer 3, Adventurer 4

```typescript
printers.forEach(printer => {
if (printer.protocolFormat === DiscoveryProtocol.Modern) {
console.log('Modern printer - full metadata available');
console.log(` Serial: ${printer.serialNumber}`);
console.log(` HTTP API: ${printer.ipAddress}:${printer.eventPort}`);
} else if (printer.protocolFormat === DiscoveryProtocol.Legacy) {
console.log('Legacy printer - basic metadata');
}
});
```

## Type Reference

### PrinterModel Enum

```typescript
enum PrinterModel {
AD5X = 'AD5X',
Adventurer5M = 'Adventurer5M',
Adventurer5MPro = 'Adventurer5MPro',
Adventurer4 = 'Adventurer4',
Adventurer3 = 'Adventurer3',
Unknown = 'Unknown'
}
```

### DiscoveryProtocol Enum

```typescript
enum DiscoveryProtocol {
Modern = 'modern', // 276-byte responses
Legacy = 'legacy' // 140-byte responses
}
```

### PrinterStatus Enum

```typescript
enum PrinterStatus {
Ready = 0,
Busy = 1,
Error = 2,
Unknown = 3
}
```

### DiscoveredPrinter Interface

```typescript
interface DiscoveredPrinter {
model: PrinterModel;
protocolFormat: DiscoveryProtocol;
name: string;
ipAddress: string;
commandPort: number;
serialNumber?: string; // Modern protocol only
eventPort?: number; // Modern protocol only (typically 8898)
vendorId?: number;
productId?: number;
productType?: number; // Modern protocol only
statusCode?: number;
status?: PrinterStatus;
}
```

### DiscoveryOptions Interface

```typescript
interface DiscoveryOptions {
timeout?: number; // Default: 10000
idleTimeout?: number; // Default: 1500
maxRetries?: number; // Default: 3
useMulticast?: boolean; // Default: true
useBroadcast?: boolean; // Default: true
ports?: number[]; // Default: [8899, 19000, 48899]
}
```

## Full Migration Checklist

- [ ] Update imports from `FlashForgePrinterDiscovery` to `PrinterDiscovery`
- [ ] Change `discoverPrintersAsync()` calls to `discover()`
- [ ] Update parameter style from positional to options object
- [ ] Replace `FlashForgePrinter` type with `DiscoveredPrinter`
- [ ] Replace `isAD5X` boolean checks with `model` enum comparisons
- [ ] Update any destructuring to use new property names
- [ ] Remove `toString()` calls (not available on interface)
- [ ] Test with all printer models you support
- [ ] Update any documentation or examples

## Need Help?

- **New API Documentation**: See `docs/README.md` and `docs/clients.md`
- **Full Specification**: See `docs/specs/printer-discovery.md`
- **Type Definitions**: See `src/models/PrinterDiscovery.ts`
- **Example Usage**: See test files in `src/api/PrinterDiscovery.test.ts`
11 changes: 7 additions & 4 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,16 @@ pnpm add @ghosttypes/ff-api
To start interacting with a printer, you first need to discover it on your local network.

```typescript
import { FlashForgePrinterDiscovery } from '@ghosttypes/ff-api';
import { PrinterDiscovery } from '@ghosttypes/ff-api';

const discovery = new FlashForgePrinterDiscovery();
const printers = await discovery.discoverPrintersAsync();
const discovery = new PrinterDiscovery();
const printers = await discovery.discover();

printers.forEach(printer => {
console.log(`Found printer: ${printer.name} at ${printer.ipAddress}`);
console.log(`Found ${printer.model}: ${printer.name} at ${printer.ipAddress}`);
if (printer.serialNumber) {
console.log(` Serial: ${printer.serialNumber}`);
}
});
```

Expand Down
57 changes: 48 additions & 9 deletions docs/clients.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,22 +123,61 @@ Sets the extruder target temperature.

---

## FlashForgePrinterDiscovery
## PrinterDiscovery

A utility class for finding printers on the local network.
A utility class for finding FlashForge printers on the local network via UDP multicast/broadcast. Supports all FlashForge models including AD5X, 5M, 5M Pro, Adventurer 4, and Adventurer 3.

### Constructor

```typescript
constructor()
```

### Methods

#### `discoverPrintersAsync()`
#### `discover()`

```typescript
public async discoverPrintersAsync(timeoutMs: number = 10000, idleTimeoutMs: number = 1500, maxRetries: number = 3): Promise<FlashForgePrinter[]>
public async discover(options?: DiscoveryOptions): Promise<DiscoveredPrinter[]>
```

Broadcasts a discovery packet via UDP and listens for responses.
Discovers printers on the local network using UDP multicast and broadcast.

- **`timeoutMs`**: Max time to wait for responses.
- **`idleTimeoutMs`**: Time to wait after the last response before returning.
- **`maxRetries`**: Number of broadcast attempts if no printers are found initially.
**Options:**
- **`timeout`** (number): Total time to wait for responses (default: 10000ms)
- **`idleTimeout`** (number): Time to wait after last response (default: 1500ms)
- **`maxRetries`** (number): Maximum retry attempts (default: 3)
- **`useMulticast`** (boolean): Use multicast discovery (default: true)
- **`useBroadcast`** (boolean): Use broadcast discovery (default: true)
- **`ports`** (number[]): Specific ports to scan (default: [8899, 19000, 48899])

**Returns:** An array of `FlashForgePrinter` objects containing IP, Serial, and Name.
**Returns:** An array of `DiscoveredPrinter` objects with comprehensive printer information.

#### `monitor()`

```typescript
public monitor(options?: DiscoveryOptions): EventEmitter
```

Starts continuous monitoring for printers, emitting events as printers are discovered.

**Returns:** EventEmitter that emits:
- **`discovered`**: Emitted for each new printer found
- **`end`**: Emitted when monitoring completes
- **`error`**: Emitted on errors

### Example

```typescript
import { PrinterDiscovery } from '@ghosttypes/ff-api';

const discovery = new PrinterDiscovery();
const printers = await discovery.discover({ timeout: 5000 });

printers.forEach(printer => {
console.log(`${printer.model}: ${printer.name}`);
console.log(` IP: ${printer.ipAddress}:${printer.commandPort}`);
console.log(` Serial: ${printer.serialNumber || 'N/A'}`);
console.log(` Status: ${printer.status}`);
});
```
Loading