Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d9029ec02b | |||
| b89e8cbc3c | |||
| 716347bac1 | |||
| a6ee36f187 | |||
| be993bf667 | |||
| 1cc8c48315 |
23
changelog.md
23
changelog.md
@@ -1,5 +1,28 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2026-01-13 - 3.1.0 - feat(print)
|
||||||
|
use IPP smartPrint and normalize IPP capabilities and job mapping
|
||||||
|
|
||||||
|
- Use IppProtocol.smartPrint for automatic format detection/conversion when submitting print jobs.
|
||||||
|
- Normalize and map IIppJob -> IPrintJob via mapIppJobToInternal, collapsing extended IPP job states into internal states.
|
||||||
|
- Parse IIppPrinterCapabilities fields (mediaSizeSupported, mediaTypeSupported, sidesSupported, printQualitySupported, copiesSupported) and derive supportsDuplex from sidesSupported and maxCopies from copiesSupported range with a fallback.
|
||||||
|
- Map numeric IPP printQuality values (3,4,5) to internal quality strings (draft, normal, high).
|
||||||
|
- Switched calls to getPrinterAttributes/getJobAttributes and adjusted job listing to map returned IIppJob objects.
|
||||||
|
- Export new IPP types from protocols index: IIppPrinterCapabilities, IIppJob, IIppPrintOptions.
|
||||||
|
|
||||||
|
## 2026-01-12 - 3.0.2 - fix(devicemanager)
|
||||||
|
no changes detected - nothing to commit
|
||||||
|
|
||||||
|
- git diff indicates no modifications, additions, or deletions
|
||||||
|
- no files were changed in the provided diff
|
||||||
|
|
||||||
|
## 2026-01-12 - 3.0.1 - fix(release)
|
||||||
|
add npm registries to release config and expand documentation for UniversalDevice architecture and smart-home features
|
||||||
|
|
||||||
|
- npmextra.json: add "registries" to release configuration to publish to both Verdaccio (https://verdaccio.lossless.digital) and the npm registry
|
||||||
|
- readme.hints.md: rewritten/expanded implementation notes to describe the UniversalDevice architecture, composable features (including smart-home types like light, switch, sensor, climate, cover, lock, fan, camera), protocols, discovery, factories, interfaces, and testing guidance
|
||||||
|
- readme.md: add factory usage examples including smart-home factory functions, update txtRecords usage (rp) in examples, and small copy/emoji edits
|
||||||
|
|
||||||
## 2026-01-10 - 3.0.0 - BREAKING CHANGE(devicemanager)
|
## 2026-01-10 - 3.0.0 - BREAKING CHANGE(devicemanager)
|
||||||
migrate tests to new UniversalDevice/feature-based API, add device factories, SNMP protocol/feature and IP helper utilities
|
migrate tests to new UniversalDevice/feature-based API, add device factories, SNMP protocol/feature and IP helper utilities
|
||||||
|
|
||||||
|
|||||||
@@ -11,10 +11,14 @@
|
|||||||
"projectDomain": "ecobridge.xyz"
|
"projectDomain": "ecobridge.xyz"
|
||||||
},
|
},
|
||||||
"release": {
|
"release": {
|
||||||
"accessLevel": "public"
|
"accessLevel": "public",
|
||||||
|
"registries": [
|
||||||
|
"https://verdaccio.lossless.digital",
|
||||||
|
"https://registry.npmjs.org"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@ship.zone/szci": {
|
"@ship.zone/szci": {
|
||||||
"npmGlobalTools": []
|
"npmGlobalTools": []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ecobridge.xyz/devicemanager",
|
"name": "@ecobridge.xyz/devicemanager",
|
||||||
"version": "3.0.0",
|
"version": "3.1.0",
|
||||||
"private": false,
|
"private": false,
|
||||||
"description": "a device manager for talking to devices on network and over usb",
|
"description": "a device manager for talking to devices on network and over usb",
|
||||||
"main": "dist_ts/index.js",
|
"main": "dist_ts/index.js",
|
||||||
|
|||||||
107
readme.hints.md
107
readme.hints.md
@@ -2,20 +2,20 @@
|
|||||||
|
|
||||||
## Architecture Overview
|
## Architecture Overview
|
||||||
|
|
||||||
The device manager supports two architectures:
|
The device manager uses a **UniversalDevice** architecture with composable features.
|
||||||
|
|
||||||
### Legacy Architecture (Still Supported)
|
### Key Concepts
|
||||||
- Separate device classes: `Scanner`, `Printer`, `Speaker`, `SnmpDevice`, `UpsDevice`, `DlnaRenderer`, `DlnaServer`
|
|
||||||
- Type-specific collections in DeviceManager
|
|
||||||
- Type-based queries: `getScanners()`, `getPrinters()`, `getSpeakers()`
|
|
||||||
|
|
||||||
### New Universal Device Architecture
|
- **UniversalDevice**: A single device class that can have multiple features attached
|
||||||
- Single `UniversalDevice` class with composable features
|
- **Features**: Composable capabilities (scan, print, playback, volume, power, snmp, smart home, etc.)
|
||||||
- Features are capabilities that can be attached to any device
|
- **Protocols**: Low-level protocol implementations (eSCL, SANE, IPP, SNMP, NUT, UPnP, Home Assistant)
|
||||||
- Supports multifunction devices naturally (e.g., printer+scanner)
|
|
||||||
|
|
||||||
## Key Files
|
## Key Files
|
||||||
|
|
||||||
|
### Core (`ts/`)
|
||||||
|
- `devicemanager.classes.devicemanager.ts` - Main DeviceManager class with discovery and device registry
|
||||||
|
- `device/device.classes.device.ts` - UniversalDevice class with feature management
|
||||||
|
|
||||||
### Features (`ts/features/`)
|
### Features (`ts/features/`)
|
||||||
- `feature.abstract.ts` - Base Feature class with connection management and retry logic
|
- `feature.abstract.ts` - Base Feature class with connection management and retry logic
|
||||||
- `feature.scan.ts` - Scanning via eSCL/SANE protocols
|
- `feature.scan.ts` - Scanning via eSCL/SANE protocols
|
||||||
@@ -24,43 +24,92 @@ The device manager supports two architectures:
|
|||||||
- `feature.volume.ts` - Volume control (separate from playback)
|
- `feature.volume.ts` - Volume control (separate from playback)
|
||||||
- `feature.power.ts` - UPS/power monitoring via NUT/SNMP
|
- `feature.power.ts` - UPS/power monitoring via NUT/SNMP
|
||||||
- `feature.snmp.ts` - SNMP queries
|
- `feature.snmp.ts` - SNMP queries
|
||||||
|
- `feature.light.ts` - Smart light control
|
||||||
|
- `feature.switch.ts` - Smart switch control
|
||||||
|
- `feature.sensor.ts` - Smart sensors
|
||||||
|
- `feature.climate.ts` - Climate/HVAC control
|
||||||
|
- `feature.cover.ts` - Blinds, garage doors
|
||||||
|
- `feature.lock.ts` - Smart locks
|
||||||
|
- `feature.fan.ts` - Fan control
|
||||||
|
- `feature.camera.ts` - Camera control
|
||||||
|
|
||||||
### Device (`ts/device/`)
|
### Protocols (`ts/protocols/`)
|
||||||
- `device.classes.device.ts` - UniversalDevice class with feature management
|
- `protocol.escl.ts` - eSCL/AirScan scanner protocol
|
||||||
|
- `protocol.sane.ts` - SANE network scanner protocol
|
||||||
|
- `protocol.ipp.ts` - IPP printer protocol
|
||||||
|
- `protocol.snmp.ts` - SNMP queries
|
||||||
|
- `protocol.nut.ts` - Network UPS Tools protocol
|
||||||
|
- `protocol.upnp.ts` - UPnP/SOAP client
|
||||||
|
- `protocol.upssnmp.ts` - UPS-specific SNMP
|
||||||
|
- `protocol.homeassistant.ts` - Home Assistant WebSocket API
|
||||||
|
|
||||||
|
### Discovery (`ts/discovery/`)
|
||||||
|
- `discovery.classes.mdns.ts` - mDNS discovery (Bonjour)
|
||||||
|
- `discovery.classes.ssdp.ts` - SSDP/UPnP discovery
|
||||||
|
- `discovery.classes.networkscanner.ts` - Active network scanning
|
||||||
|
- `discovery.classes.homeassistant.ts` - Home Assistant instance discovery
|
||||||
|
|
||||||
|
### Factories (`ts/factories/`)
|
||||||
|
- `index.ts` - Device factory functions for creating pre-configured devices:
|
||||||
|
- `createScanner`, `createPrinter`, `createSpeaker`, `createDlnaRenderer`
|
||||||
|
- `createSnmpDevice`, `createUpsDevice`
|
||||||
|
- Smart home: `createSmartLight`, `createSmartSwitch`, `createSmartSensor`, etc.
|
||||||
|
|
||||||
### Interfaces (`ts/interfaces/`)
|
### Interfaces (`ts/interfaces/`)
|
||||||
- `feature.interfaces.ts` - All feature-related types and interfaces
|
- `feature.interfaces.ts` - All feature-related types and interfaces
|
||||||
- `index.ts` - Re-exports feature interfaces
|
- `smarthome.interfaces.ts` - Smart home specific interfaces
|
||||||
|
- `homeassistant.interfaces.ts` - Home Assistant API types
|
||||||
|
- `index.ts` - Re-exports all interfaces
|
||||||
|
|
||||||
## Feature Types
|
## Feature Types
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
type TFeatureType =
|
type TFeatureType =
|
||||||
| 'scan' | 'print' | 'fax' | 'copy'
|
| 'scan' | 'print' | 'fax' | 'copy'
|
||||||
| 'playback' | 'volume' | 'power' | 'snmp'
|
| 'playback' | 'volume' | 'power' | 'snmp'
|
||||||
| 'dlna-render' | 'dlna-serve';
|
| 'dlna-render' | 'dlna-serve'
|
||||||
|
| 'light' | 'climate' | 'sensor' | 'camera'
|
||||||
|
| 'cover' | 'switch' | 'lock' | 'fan';
|
||||||
```
|
```
|
||||||
|
|
||||||
## DeviceManager Feature API
|
## DeviceManager API
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// Query by features
|
// Query by features
|
||||||
dm.getDevicesWithFeature('scan'); // Devices with scan feature
|
dm.getDevices(); // All devices
|
||||||
|
dm.getDevices({ hasFeature: 'scan' }); // Devices with scan feature
|
||||||
|
dm.getDevices({ name: 'Brother' }); // Devices matching name
|
||||||
dm.getDevicesWithFeatures(['scan', 'print']); // Devices with ALL features
|
dm.getDevicesWithFeatures(['scan', 'print']); // Devices with ALL features
|
||||||
dm.getDevicesWithAnyFeature(['playback', 'volume']); // Devices with ANY feature
|
dm.getDevicesWithAnyFeature(['playback', 'volume']); // Devices with ANY feature
|
||||||
|
|
||||||
// Manage universal devices
|
// Select (throws if not found)
|
||||||
dm.addUniversalDevice(device);
|
dm.selectDevice({ address: '192.168.1.100' });
|
||||||
dm.addFeatureToDevice(deviceId, feature);
|
|
||||||
dm.removeFeatureFromDevice(deviceId, featureType);
|
// Discovery
|
||||||
|
dm.discoverScanners('192.168.1.0/24');
|
||||||
|
dm.discoverPrinters('192.168.1.0/24');
|
||||||
|
dm.scanNetwork({ ipRange: '...', probeEscl: true, ... });
|
||||||
|
dm.startDiscovery(); // mDNS/SSDP
|
||||||
|
dm.stopDiscovery();
|
||||||
```
|
```
|
||||||
|
|
||||||
## Protocol Implementations
|
## UniversalDevice API
|
||||||
- `EsclProtocol` - eSCL/AirScan scanner protocol
|
|
||||||
- `SaneProtocol` - SANE network scanner protocol
|
|
||||||
- `IppProtocol` - IPP printer protocol
|
|
||||||
- `SnmpProtocol` - SNMP queries
|
|
||||||
- `NutProtocol` - Network UPS Tools protocol
|
|
||||||
|
|
||||||
## Type Notes
|
```typescript
|
||||||
- `TScanFormat` includes 'tiff' (added for compatibility)
|
// Feature access
|
||||||
- `IPrinterCapabilities` (from index.ts) has `string[]` for sides/quality
|
device.hasFeature('scan');
|
||||||
- `IPrintCapabilities` (from feature.interfaces.ts) has typed arrays
|
device.getFeature<ScanFeature>('scan'); // Returns undefined if not found
|
||||||
|
device.selectFeature<ScanFeature>('scan'); // Throws if not found
|
||||||
|
device.getFeatureTypes(); // ['scan', 'print', ...]
|
||||||
|
|
||||||
|
// Connection
|
||||||
|
await device.connect(); // Connect all features
|
||||||
|
await device.disconnect(); // Disconnect all features
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing Notes
|
||||||
|
|
||||||
|
- Test files use `@git.zone/tstest` with tap-based assertions
|
||||||
|
- Import `expect` from `@git.zone/tstest/tapbundle`
|
||||||
|
- Tests are in `test/` directory
|
||||||
|
- Run with `pnpm test` or `tstest test/test.some.ts --verbose`
|
||||||
|
|||||||
39
readme.md
39
readme.md
@@ -468,6 +468,8 @@ type TFeatureType =
|
|||||||
|
|
||||||
### Custom Device Creation
|
### Custom Device Creation
|
||||||
|
|
||||||
|
Use the factory functions for creating devices with specific features:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { createScanner, createPrinter, createSpeaker } from '@ecobridge.xyz/devicemanager';
|
import { createScanner, createPrinter, createSpeaker } from '@ecobridge.xyz/devicemanager';
|
||||||
|
|
||||||
@@ -487,8 +489,7 @@ const printer = createPrinter({
|
|||||||
name: 'Office Printer',
|
name: 'Office Printer',
|
||||||
address: '192.168.1.51',
|
address: '192.168.1.51',
|
||||||
port: 631,
|
port: 631,
|
||||||
ippPath: '/ipp/print',
|
txtRecords: { rp: '/ipp/print' },
|
||||||
txtRecords: {},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create a Sonos speaker
|
// Create a Sonos speaker
|
||||||
@@ -498,7 +499,37 @@ const speaker = createSpeaker({
|
|||||||
address: '192.168.1.52',
|
address: '192.168.1.52',
|
||||||
port: 1400,
|
port: 1400,
|
||||||
protocol: 'sonos',
|
protocol: 'sonos',
|
||||||
txtRecords: {},
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Smart Home Factory Functions
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import {
|
||||||
|
createSmartLight,
|
||||||
|
createSmartSwitch,
|
||||||
|
createSmartSensor,
|
||||||
|
createSmartClimate,
|
||||||
|
createSmartCover,
|
||||||
|
createSmartLock,
|
||||||
|
createSmartFan,
|
||||||
|
createSmartCamera,
|
||||||
|
} from '@ecobridge.xyz/devicemanager';
|
||||||
|
|
||||||
|
// Create devices with Home Assistant integration
|
||||||
|
const light = createSmartLight({
|
||||||
|
id: 'living-room-light',
|
||||||
|
name: 'Living Room Light',
|
||||||
|
address: 'homeassistant.local',
|
||||||
|
port: 8123,
|
||||||
|
entityId: 'light.living_room',
|
||||||
|
protocol: 'home-assistant',
|
||||||
|
protocolClient: haClient, // Your HomeAssistantProtocol instance
|
||||||
|
capabilities: {
|
||||||
|
supportsBrightness: true,
|
||||||
|
supportsColorTemp: true,
|
||||||
|
supportsRgb: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -563,7 +594,7 @@ const maybePrint = device.getFeature<PrintFeature>('print'); // undefined
|
|||||||
|
|
||||||
## 🙏 Credits
|
## 🙏 Credits
|
||||||
|
|
||||||
Built with love using:
|
Built with ❤️ using:
|
||||||
- [bonjour-service](https://github.com/onlxltd/bonjour-service) - mDNS discovery
|
- [bonjour-service](https://github.com/onlxltd/bonjour-service) - mDNS discovery
|
||||||
- [node-ssdp](https://github.com/diversario/node-ssdp) - SSDP/UPnP discovery
|
- [node-ssdp](https://github.com/diversario/node-ssdp) - SSDP/UPnP discovery
|
||||||
- [net-snmp](https://github.com/markabrahams/node-net-snmp) - SNMP protocol
|
- [net-snmp](https://github.com/markabrahams/node-net-snmp) - SNMP protocol
|
||||||
|
|||||||
@@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@ecobridge.xyz/devicemanager',
|
name: '@ecobridge.xyz/devicemanager',
|
||||||
version: '3.0.0',
|
version: '3.1.0',
|
||||||
description: 'a device manager for talking to devices on network and over usb'
|
description: 'a device manager for talking to devices on network and over usb'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Feature, type TDeviceReference } from './feature.abstract.js';
|
import { Feature, type TDeviceReference } from './feature.abstract.js';
|
||||||
import { IppProtocol } from '../protocols/index.js';
|
import { IppProtocol, type IIppPrinterCapabilities, type IIppJob } from '../protocols/index.js';
|
||||||
import type {
|
import type {
|
||||||
TPrintProtocol,
|
TPrintProtocol,
|
||||||
TPrintSides,
|
TPrintSides,
|
||||||
@@ -16,7 +16,6 @@ import type {
|
|||||||
IPrintFeatureInfo,
|
IPrintFeatureInfo,
|
||||||
IFeatureOptions,
|
IFeatureOptions,
|
||||||
} from '../interfaces/feature.interfaces.js';
|
} from '../interfaces/feature.interfaces.js';
|
||||||
import type { IPrinterCapabilities } from '../interfaces/index.js';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Options for creating a PrintFeature
|
* Options for creating a PrintFeature
|
||||||
@@ -101,7 +100,7 @@ export class PrintFeature extends Feature {
|
|||||||
|
|
||||||
this.ippClient = new IppProtocol(address, port, path);
|
this.ippClient = new IppProtocol(address, port, path);
|
||||||
// Verify connection by getting printer attributes
|
// Verify connection by getting printer attributes
|
||||||
const attrs = await this.ippClient.getAttributes();
|
const attrs = await this.ippClient.getPrinterAttributes();
|
||||||
this.updateCapabilitiesFromIpp(attrs);
|
this.updateCapabilitiesFromIpp(attrs);
|
||||||
}
|
}
|
||||||
// JetDirect and LPD don't need connection verification
|
// JetDirect and LPD don't need connection verification
|
||||||
@@ -154,7 +153,8 @@ export class PrintFeature extends Feature {
|
|||||||
throw new Error('Print feature not connected');
|
throw new Error('Print feature not connected');
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.ippClient.getJobs();
|
const jobs = await this.ippClient.getJobs();
|
||||||
|
return jobs.map(job => this.mapIppJobToInternal(job));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -165,7 +165,8 @@ export class PrintFeature extends Feature {
|
|||||||
throw new Error('Print feature not connected');
|
throw new Error('Print feature not connected');
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.ippClient.getJobInfo(jobId);
|
const job = await this.ippClient.getJobAttributes(jobId);
|
||||||
|
return this.mapIppJobToInternal(job);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -191,9 +192,17 @@ export class PrintFeature extends Feature {
|
|||||||
|
|
||||||
this.emit('print:started', options);
|
this.emit('print:started', options);
|
||||||
|
|
||||||
// IppProtocol.print() accepts IPrintOptions and returns IPrintJob
|
// Use smartPrint for auto format detection and conversion
|
||||||
const job = await this.ippClient.print(data, options);
|
const ippJob = await this.ippClient.smartPrint(data, {
|
||||||
|
jobName: options?.jobName,
|
||||||
|
copies: options?.copies,
|
||||||
|
media: options?.mediaSize,
|
||||||
|
sides: options?.sides,
|
||||||
|
printQuality: options?.quality,
|
||||||
|
colorMode: options?.colorMode,
|
||||||
|
});
|
||||||
|
|
||||||
|
const job = this.mapIppJobToInternal(ippJob);
|
||||||
this.emit('print:submitted', job);
|
this.emit('print:submitted', job);
|
||||||
return job;
|
return job;
|
||||||
}
|
}
|
||||||
@@ -202,58 +211,57 @@ export class PrintFeature extends Feature {
|
|||||||
// Helper Methods
|
// Helper Methods
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
private updateCapabilitiesFromIpp(caps: IPrinterCapabilities): void {
|
private updateCapabilitiesFromIpp(caps: IIppPrinterCapabilities): void {
|
||||||
this.supportsColor = caps.colorSupported;
|
this.supportsColor = caps.colorSupported;
|
||||||
this.supportsDuplex = caps.duplexSupported;
|
// Derive duplexSupported from sidesSupported
|
||||||
this.maxCopies = caps.maxCopies;
|
this.supportsDuplex = caps.sidesSupported?.some(s =>
|
||||||
|
s.includes('two-sided')
|
||||||
|
) ?? false;
|
||||||
|
// Get max copies from range
|
||||||
|
this.maxCopies = caps.copiesSupported?.upper ?? 99;
|
||||||
|
|
||||||
if (caps.mediaSizes && caps.mediaSizes.length > 0) {
|
if (caps.mediaSizeSupported && caps.mediaSizeSupported.length > 0) {
|
||||||
this.supportedMediaSizes = caps.mediaSizes;
|
this.supportedMediaSizes = caps.mediaSizeSupported;
|
||||||
}
|
}
|
||||||
if (caps.mediaTypes && caps.mediaTypes.length > 0) {
|
if (caps.mediaTypeSupported && caps.mediaTypeSupported.length > 0) {
|
||||||
this.supportedMediaTypes = caps.mediaTypes;
|
this.supportedMediaTypes = caps.mediaTypeSupported;
|
||||||
}
|
}
|
||||||
if (caps.sidesSupported && caps.sidesSupported.length > 0) {
|
if (caps.sidesSupported && caps.sidesSupported.length > 0) {
|
||||||
this.supportedSides = caps.sidesSupported.filter((s): s is TPrintSides =>
|
this.supportedSides = caps.sidesSupported.filter((s): s is TPrintSides =>
|
||||||
['one-sided', 'two-sided-long-edge', 'two-sided-short-edge'].includes(s)
|
['one-sided', 'two-sided-long-edge', 'two-sided-short-edge'].includes(s)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (caps.qualitySupported && caps.qualitySupported.length > 0) {
|
// Map IPP quality values (3=draft, 4=normal, 5=high) to strings
|
||||||
this.supportedQualities = caps.qualitySupported.filter((q): q is TPrintQuality =>
|
if (caps.printQualitySupported && caps.printQualitySupported.length > 0) {
|
||||||
['draft', 'normal', 'high'].includes(q)
|
const qualityMap: Record<number, TPrintQuality> = { 3: 'draft', 4: 'normal', 5: 'high' };
|
||||||
);
|
this.supportedQualities = caps.printQualitySupported
|
||||||
|
.map(q => qualityMap[q])
|
||||||
|
.filter((q): q is TPrintQuality => q !== undefined);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private qualityToIpp(quality: TPrintQuality): number {
|
/**
|
||||||
switch (quality) {
|
* Map IIppJob to IPrintJob, normalizing extended states
|
||||||
case 'draft': return 3;
|
*/
|
||||||
case 'normal': return 4;
|
private mapIppJobToInternal(job: IIppJob): IPrintJob {
|
||||||
case 'high': return 5;
|
// Map extended IPP states to simpler internal states
|
||||||
default: return 4;
|
const stateMap: Record<IIppJob['state'], IPrintJob['state']> = {
|
||||||
}
|
'pending': 'pending',
|
||||||
}
|
'pending-held': 'pending',
|
||||||
|
'processing': 'processing',
|
||||||
private mapIppJob(job: Record<string, unknown>): IPrintJob {
|
'processing-stopped': 'processing',
|
||||||
const stateMap: Record<number, IPrintJob['state']> = {
|
'canceled': 'canceled',
|
||||||
3: 'pending',
|
'aborted': 'aborted',
|
||||||
4: 'pending',
|
'completed': 'completed',
|
||||||
5: 'processing',
|
|
||||||
6: 'processing',
|
|
||||||
7: 'canceled',
|
|
||||||
8: 'aborted',
|
|
||||||
9: 'completed',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: job['job-id'] as number,
|
id: job.id,
|
||||||
name: job['job-name'] as string ?? 'Unknown',
|
name: job.name,
|
||||||
state: stateMap[(job['job-state'] as number) ?? 3] ?? 'pending',
|
state: stateMap[job.state],
|
||||||
stateReason: (job['job-state-reasons'] as string[])?.[0],
|
stateReason: job.stateReasons?.[0],
|
||||||
createdAt: new Date((job['time-at-creation'] as number) * 1000),
|
createdAt: job.createdAt,
|
||||||
completedAt: job['time-at-completed']
|
completedAt: job.completedAt,
|
||||||
? new Date((job['time-at-completed'] as number) * 1000)
|
|
||||||
: undefined,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,12 @@ export { EsclProtocol } from './protocol.escl.js';
|
|||||||
export { SaneProtocol } from './protocol.sane.js';
|
export { SaneProtocol } from './protocol.sane.js';
|
||||||
|
|
||||||
// IPP printer protocol
|
// IPP printer protocol
|
||||||
export { IppProtocol } from './protocol.ipp.js';
|
export {
|
||||||
|
IppProtocol,
|
||||||
|
type IIppPrinterCapabilities,
|
||||||
|
type IIppJob,
|
||||||
|
type IIppPrintOptions,
|
||||||
|
} from './protocol.ipp.js';
|
||||||
|
|
||||||
// SNMP query protocol
|
// SNMP query protocol
|
||||||
export {
|
export {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user