# Memobird SDK

TypeScript SDK for [Memobird (咕咕机)](http://www.memobird.cn) thermal printer API. This SDK provides a complete, type-safe interface for controlling Memobird devices with comprehensive error handling and automatic retry capabilities.

## Features

- ✅ **Full TypeScript support** with comprehensive type definitions
- ✅ **Automatic GBK encoding** for Chinese characters
- ✅ **Retry mechanism** with exponential backoff
- ✅ **Image processing** utilities for converting JPG/PNG to monochrome bitmap
- ✅ **Complete JSDoc** documentation for all functions
- ✅ **ESM module** format
- ✅ **Error handling** with custom error classes
- ✅ **Promise-based** async/await API

## Installation

```bash
npm install memobirdsdk
```

For examples, also install dotenv to manage environment variables:

```bash
npm install dotenv
```

## Quick Start

```typescript
import { MemobirdClient } from 'memobirdsdk';

// Initialize the client
const client = new MemobirdClient({
  ak: 'your-access-key-from-open.memobird.cn'
});

// Bind user to device
const bindResult = await client.bindUser('device-id', 'user-123');
const userId = bindResult.showapi_userid!;

// Print text (auto-converts Chinese to GBK)
await client.printText('Hello World! 你好世界！', 'device-id', userId);
```

### Using Environment Variables (Recommended)

For better security, use environment variables to store sensitive information:

1. Create a `.env` file in your project root:

```env
MEMOBIRD_AK=your-access-key-here
MEMOBIRD_DEVICE_ID=your-device-id-here
MEMOBIRD_USER_ID=your-user-identifier-here
```

2. Use dotenv in your code:

```typescript
import 'dotenv/config';
import { MemobirdClient } from 'memobirdsdk';

const client = new MemobirdClient({
  ak: process.env.MEMOBIRD_AK!
});

const deviceId = process.env.MEMOBIRD_DEVICE_ID!;
const userId = process.env.MEMOBIRD_USER_ID!;

await client.printText('Hello!', deviceId, parseInt(userId, 10));
```

**Important**: Add `.env` to your `.gitignore` to avoid committing sensitive credentials!

## Testing Your Setup

To quickly test if everything is working, use the built-in test function:

```bash
npm test
```

This will:
- ✅ Verify your environment variables are set
- ✅ Test the API connection and user binding
- ✅ Print a debug slip with system information
- ✅ Display detailed status and error messages

The printed slip will include:
- Current date and time
- System information (hostname, platform, Node.js version)
- Network status
- Device and user IDs
- API call results

### Available Scripts

```bash
npm test                 # Run test and print debug slip
npm run example:basic    # Run basic usage example
npm run example:text     # Run text printing examples
npm run example:image    # Run image printing examples
npm run example:advanced # Run advanced usage examples
```

## Getting Started

### 1. Get Your Access Key

1. Visit [open.memobird.cn](http://open.memobird.cn)
2. Register as a developer
3. Get your Access Key (ak) from your user dashboard

### 2. Get Your Device ID

Double-click your Memobird device to print out the device ID.

### 3. Bind User to Device

```typescript
const client = new MemobirdClient({ ak: 'your-access-key' });

const bindResult = await client.bindUser('fb93bfff504c020a', 'user-123');
console.log('User ID:', bindResult.showapi_userid);
```

## API Reference

### MemobirdClient

#### Constructor

```typescript
new MemobirdClient(config: MemobirdConfig)
```

**Parameters:**
- `config.ak` (required): Access Key from open.memobird.cn
- `config.baseUrl` (optional): API base URL (default: `'http://open.memobird.cn/home'`)
- `config.timeout` (optional): Request timeout in milliseconds (default: `30000`)
- `config.retry` (optional): Retry configuration
  - `maxRetries`: Maximum retry attempts (default: `3`)
  - `initialDelay`: Initial delay before first retry in ms (default: `1000`)
  - `backoffMultiplier`: Exponential backoff multiplier (default: `2`)
  - `maxDelay`: Maximum delay between retries in ms (default: `10000`)
  - `retryStatusCodes`: HTTP status codes to retry (default: `[408, 429, 500, 502, 503, 504]`)

**Example:**
```typescript
const client = new MemobirdClient({
  ak: 'your-access-key',
  timeout: 60000,
  retry: {
    maxRetries: 5,
    initialDelay: 2000
  }
});
```

#### Methods

##### bindUser(memobirdID, useridentifying)

Binds a user identifier to a device.

```typescript
await client.bindUser(memobirdID: string, useridentifying: string): Promise<BindUserResponse>
```

**Returns:** `{ showapi_res_code, showapi_res_error, showapi_userid }`

##### printText(text, memobirdID, userID)

Prints text content. Automatically converts UTF-8 to GBK encoding for Chinese characters.

```typescript
await client.printText(text: string, memobirdID: string, userID: number): Promise<PrintResponse>
```

**Returns:** `{ showapi_res_code, showapi_res_error, result, printcontentid, smartGuid }`

##### printImage(base64Image, memobirdID, userID)

Prints monochrome bitmap image.

```typescript
await client.printImage(base64Image: string, memobirdID: string, userID: number): Promise<PrintResponse>
```

**Note:** Image must be converted to monochrome bitmap first using `convertImageToMonochrome()`.

##### printFromUrl(url, memobirdID, userID)

Prints content from a web URL.

```typescript
await client.printFromUrl(url: string, memobirdID: string, userID: number): Promise<PrintResponse>
```

**Note:** Works best with static or server-rendered pages. Images must use complete URLs.

##### printFromHtml(html, memobirdID, userID)

Prints content from HTML source.

```typescript
await client.printFromHtml(html: string, memobirdID: string, userID: number): Promise<PrintResponse>
```

**Note:** CSS must be inline, images must use complete URLs.

##### getPrintStatus(printcontentid)

Gets print status of a submitted job.

```typescript
await client.getPrintStatus(printcontentid: string | number): Promise<PrintStatusResponse>
```

**Returns:** `{ showapi_res_code, showapi_res_error, printflag, printcontentID }`
- `printflag === 1`: Printed successfully
- `printflag === 0`: Pending or failed

##### convertImageToMonochrome(base64Image)

Converts JPG/PNG to monochrome bitmap for printing.

```typescript
await client.convertImageToMonochrome(base64Image: string): Promise<ConvertImageResponse>
```

**Returns:** `{ showapi_res_code, showapi_res_error, result }`

## Examples

### Print Text

```typescript
import { MemobirdClient } from 'memobirdsdk';

const client = new MemobirdClient({ ak: 'your-ak' });

// Simple text
await client.printText('Hello World!', 'device-id', userId);

// Chinese text (auto-converts to GBK)
await client.printText('你好世界！', 'device-id', userId);

// Multiline text
await client.printText(`Line 1
Line 2
Line 3`, 'device-id', userId);
```

### Print Image

```typescript
import { MemobirdClient, imageFileToBase64 } from 'memobirdsdk';

const client = new MemobirdClient({ ak: 'your-ak' });

// Load image from file
const jpgBase64 = await imageFileToBase64('./image.jpg');

// Convert to monochrome bitmap
const converted = await client.convertImageToMonochrome(jpgBase64);

// Print the image
await client.printImage(converted.result!, 'device-id', userId);
```

### Print HTML

```typescript
const html = `
  <!DOCTYPE html>
  <html>
    <head>
      <style>
        body { font-family: Arial; }
        h1 { font-size: 24px; }
      </style>
    </head>
    <body>
      <h1>Receipt</h1>
      <p>Total: $10.00</p>
    </body>
  </html>
`;

await client.printFromHtml(html, 'device-id', userId);
```

### Error Handling

```typescript
import { MemobirdClient, APIError, NetworkError } from 'memobirdsdk';

const client = new MemobirdClient({ ak: 'your-ak' });

try {
  await client.printText('Hello', 'device-id', userId);
} catch (error) {
  if (error instanceof APIError) {
    console.error('API Error:', error.resError);
    console.error('Code:', error.resCode);
  } else if (error instanceof NetworkError) {
    console.error('Network Error:', error.message);
  } else {
    console.error('Unknown Error:', error);
  }
}
```

### Poll Print Status

```typescript
const printResult = await client.printText('Test', 'device-id', userId);
const printId = printResult.printcontentid!;

// Poll until printed
let printed = false;
while (!printed) {
  const status = await client.getPrintStatus(printId);

  if (status.printflag === 1) {
    console.log('Printed successfully!');
    printed = true;
  } else {
    await new Promise(resolve => setTimeout(resolve, 2000));
  }
}
```

## Utility Functions

### Text Encoding

```typescript
import { textToGBKBase64, gbkBase64ToText } from 'memobirdsdk';

// Convert to GBK Base64
const encoded = textToGBKBase64('你好');

// Decode back to UTF-8
const decoded = gbkBase64ToText(encoded);
```

### Content Formatting

```typescript
import { formatTextContent, formatImageContent, formatMixedContent } from 'memobirdsdk';

// Format text with T: prefix
const textContent = formatTextContent('Hello');
// Returns: 'T:SGVsbG8='

// Format image with P: prefix
const imageContent = formatImageContent('base64-image-data');
// Returns: 'P:base64-image-data'

// Format mixed content
const mixed = formatMixedContent([
  { type: 'text', content: 'Hello' },
  { type: 'image', content: 'image-data' },
  { type: 'text', content: 'World' }
]);
// Returns: 'T:SGVsbG8=|P:image-data|T:V29ybGQ='
```

### Image Utilities

```typescript
import {
  imageFileToBase64,
  stripDataUrlPrefix,
  isValidBase64Image
} from 'memobirdsdk';

// Load image from file
const base64 = await imageFileToBase64('./image.jpg');

// Strip data URL prefix
const clean = stripDataUrlPrefix('data:image/png;base64,iVBORw0...');

// Validate Base64
if (isValidBase64Image(base64)) {
  console.log('Valid Base64');
}
```

### Timestamp

```typescript
import { generateTimestamp, isValidTimestamp } from 'memobirdsdk';

// Generate timestamp in Memobird format
const timestamp = generateTimestamp();
// Returns: '2025-12-29 14:30:45'

// Validate timestamp format
if (isValidTimestamp('2025-12-29 14:30:45')) {
  console.log('Valid format');
}
```

## Error Types

### MemobirdError

Base error class for all SDK errors.

### APIError

Thrown when API returns an error response (`showapi_res_code !== 1`).

**Properties:**
- `resCode`: The API response code
- `resError`: The API error message

### NetworkError

Thrown when network request fails.

**Properties:**
- `originalError`: The original error that caused the failure

### ValidationError

Thrown when input validation fails.

**Properties:**
- `field`: The field that failed validation

## Image Printing Tips

For best results with image printing:

1. **Image width**: Use 384 pixels (Memobird paper width)
2. **High contrast**: Black and white images work best
3. **Simple designs**: Complex images may not render well on thermal printers
4. **Conversion required**: Always convert JPG/PNG to monochrome bitmap using `convertImageToMonochrome()`

## HTML/URL Printing Tips

For HTML and URL printing:

1. **Inline CSS**: Don't use external stylesheets
2. **Complete URLs**: Use absolute URLs for images (not relative paths)
3. **Static content**: AJAX-rendered pages may not work properly
4. **Server-rendered**: Works best with server-rendered pages
5. **Content size**: Keep content size reasonable

## TypeScript Support

This SDK is written in TypeScript and provides complete type definitions. All functions, parameters, and return values are fully typed.

```typescript
import type {
  MemobirdConfig,
  PrintResponse,
  BindUserResponse
} from 'memobirdsdk';
```

## License

ISC

## Links

- [Memobird Official Website](http://www.memobird.cn)
- [Memobird Open Platform](http://open.memobird.cn)
- [API Documentation](http://open.memobird.cn) (Chinese)

## Support

For issues and feature requests, please create an issue on the GitHub repository.

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.
