# Star Micronics POS Printer Integration


**Site index:** [https://2plot.dev/llms.txt](https://2plot.dev/llms.txt) — every page on this site, as Markdown.  
**Network index:** [https://2plot.ai/llms.txt](https://2plot.ai/llms.txt) — The 2plot network; start here to discover sibling sites.  
**Sibling sites:** 11 more in The 2plot network — listed in the site index above.  
**Sitemap:** https://2plot.dev/sitemap.xml  

A comprehensive cloud-based receipt printing system for Dash applications, enabling remote printer management, real-time status monitoring, and automated order printing through the Star Micronics CloudPRNT platform.

---

### Overview

The Star Micronics POS Printer integration connects your Dash application to physical receipt printers via the **StarIO.Online** cloud service. This enables:

- **Remote Cloud Printing** - Send print jobs from anywhere via REST API
- **Real-Time Status Monitoring** - Track printer health, paper levels, and queue status
- **Automatic Order Printing** - Print receipts on successful checkout/payment
- **Web-Based Management** - Full-featured admin dashboard for printer control
- **Multiple Print Formats** - Text markup, images, QR codes, and barcodes
- **Queue Management** - Monitor and clear pending print jobs

#### Key Features

- ✅ **Zero Infrastructure** - No local print server required, fully cloud-based
- ✅ **Production Ready** - Used in live POS systems with Stripe integration
- ✅ **Multi-Printer Support** - Manage multiple printers from single dashboard
- ✅ **Mobile Responsive** - Admin dashboard works on all devices
- ✅ **Error Handling** - Comprehensive diagnostics and retry logic
- ✅ **Secure** - Admin-only access with Clerk authentication
- ✅ **Theme Aware** - Automatic dark/light mode support

---

### Supported Hardware

#### Compatible Printers

**mC-Print Series:**
- mC-Print2 (firmware 1.2+)
- mC-Print3 (firmware 1.2+, MQTT support 5.1+)

**TSP Series:**
- TSP100IV (firmware 1.0+, MQTT support 2.2+)

All printers must support the **CloudPRNT** protocol for cloud connectivity.

#### Connection Modes

- **HTTP Polling** - Compatible with all firmware versions (3-5 second intervals)
- **MQTT** - Real-time push notifications (requires newer firmware)

---

### Installation

#### Prerequisites

```bash
pip install requests python-dotenv Pillow qrcode[pil]
```

#### Required Dependencies

```text
dash>=4.2.0
dash-mantine-components>=2.8.0
requests>=2.32.5
python-dotenv>=1.2.2
Pillow>=10.0.0
qrcode[pil]>=7.4.2
```

#### GitHub Repository

The complete integration includes two core files:

- `printer_service.py` - Star Micronics API wrapper
- `pages/printer.py` - Admin management dashboard

---

### Quick Start

#### 1. StarIO.Online Account Setup

**Register for Account:**
```bash
# Visit registration page
https://www.starmicronicscloud.com

# Select your region:
US: https://api.stario.online
EU: https://eu-api.stario.online
```

**Create Device Group:**
1. Navigate to "Device Groups" in dashboard
2. Create new group (e.g., "your-restaurant")
3. Note the **Group Path** (e.g., `yourcompany`)
4. Enable settings:
   - ✅ AutoCreateDeviceQueue
   - ✅ WelcomePrint
   - ❌ RequireDeviceKey

**Generate API Key:**
1. Navigate to "API Keys"
2. Create new key with permissions:
   - ✅ PrintToDevice
   - ✅ ViewDeviceGroups
   - ✅ ViewDevice
   - ✅ FlushQueue
   - ✅ ModifyDevice
3. Save API key securely (shown only once)

#### 2. Printer Configuration

**Get CloudPRNT URL:**
```
Format: https://api.stario.online/v1/a/{GROUP_PATH}/cloudprnt
Example: https://api.stario.online/v1/a/yourcompany/cloudprnt
```

**Configure Printer:**
1. Access printer web interface (http://[PRINTER_IP])
2. Navigate to Settings → CloudPRNT
3. Enable CloudPRNT
4. Enter CloudPRNT URL
5. Set polling interval: 3-5 seconds
6. Save and restart printer

#### 3. Environment Variables

Create `.env` file in project root:

```bash
# Star Micronics Configuration
STAR_MICRONICS=your_api_key_here
STAR_GROUP_PATH=yourcompany
STAR_DEVICE_ID=ABC123DEF456  # Optional: default device
```

---

### Configuration Walkthrough

Step-by-step setup guide with visual feedback:



```python
# File: docs/dash_pos_printer/setup_guide_example.py

"""
Setup Guide Example - Star Micronics POS Printer
Interactive step-by-step configuration guide with visual feedback
"""

import dash_mantine_components as dmc
from dash import html, callback, Input, Output, State, ALL
from dash_iconify import DashIconify

# Configuration steps
STEPS = [
    {
        "title": "StarIO.Online Account",
        "description": "Register for a Star Micronics cloud account",
        "icon": "tabler:cloud",
        "details": [
            "Visit www.starmicronicscloud.com",
            "Select your region (US or EU)",
            "Complete registration form",
            "Verify your email address"
        ]
    },
    {
        "title": "Create Device Group",
        "description": "Set up a device group for your printers",
        "icon": "tabler:folder",
        "details": [
            "Log into StarIO.Online dashboard",
            "Navigate to 'Device Groups'",
            "Create new group with your business name",
            "Save the Group Path (e.g., 'yourcompany')",
            "Enable AutoCreateDeviceQueue"
        ]
    },
    {
        "title": "Generate API Key",
        "description": "Create API credentials for your application",
        "icon": "tabler:key",
        "details": [
            "Go to 'API Keys' section",
            "Click 'Create New API Key'",
            "Enable all permissions:",
            "  • PrintToDevice",
            "  • ViewDeviceGroups",
            "  • ViewDevice",
            "  • FlushQueue",
            "Save the API key securely"
        ]
    },
    {
        "title": "Configure Printer",
        "description": "Connect your printer to CloudPRNT",
        "icon": "tabler:printer",
        "details": [
            "Access printer web interface (http://[PRINTER_IP])",
            "Navigate to Settings → CloudPRNT",
            "Enable CloudPRNT",
            "Enter CloudPRNT URL:",
            "  https://api.stario.online/v1/a/[GROUP_PATH]/cloudprnt",
            "Set polling interval to 3-5 seconds",
            "Save and restart printer"
        ]
    },
    {
        "title": "Environment Setup",
        "description": "Configure your application environment",
        "icon": "tabler:settings",
        "details": [
            "Create .env file in project root",
            "Add STAR_MICRONICS=your_api_key",
            "Add STAR_GROUP_PATH=yourcompany",
            "Add STAR_DEVICE_ID=ABC123 (optional)",
            "Install required packages"
        ]
    },
    {
        "title": "Test Connection",
        "description": "Verify everything is working",
        "icon": "tabler:check",
        "details": [
            "Run test script: python printer_service.py",
            "Verify devices appear in list",
            "Send a test print",
            "Check printer receives the job",
            "Monitor print queue status"
        ]
    }
]

# Component layout
component = dmc.Container([
    # Header
    dmc.Stack([
        dmc.Group([
            dmc.ThemeIcon(
                DashIconify(icon="tabler:checklist", width=30),
                size=40,
                radius="md",
                variant="light",
                color="blue"
            ),
            dmc.Title("Setup Guide", order=2)
        ]),
        dmc.Text(
            "Step-by-step configuration guide for Star Micronics printer integration",
            c="dimmed",
            size="sm"
        )
    ], gap="xs", mb="lg"),

    # Progress indicator
    dmc.Card([
        dmc.Stack([
            dmc.Group([
                dmc.Text("Setup Progress", fw=600),
                dmc.Badge(id="progress-badge", color="blue", children="0%")
            ], justify="space-between"),
            dmc.Progress(id="progress-bar", value=0, color="blue", size="lg")
        ])
    ], shadow="sm", padding="md", radius="md", withBorder=True, mb="lg"),

    # Stepper
    dmc.Stepper(
        id="setup-stepper",
        active=0,
        orientation="vertical",
        children=[
            dmc.StepperStep(
                label=step["title"],
                description=step["description"],
                icon=DashIconify(icon=step["icon"], width=20),
                children=[
                    dmc.Card([
                        dmc.Stack([
                            dmc.Text("Instructions:", size="sm", fw=600, c="dimmed"),
                            dmc.List([
                                dmc.ListItem(detail) for detail in step["details"]
                            ], size="sm", spacing="xs"),
                            dmc.Divider(),
                            dmc.Group([
                                dmc.Button(
                                    "Previous",
                                    variant="light",
                                    color="gray",
                                    id={"type": "prev-btn", "index": i},
                                    disabled=i == 0
                                ) if i > 0 else html.Div(),
                                dmc.Button(
                                    "Next" if i < len(STEPS) - 1 else "Complete Setup",
                                    id={"type": "next-btn", "index": i},
                                    color="blue" if i < len(STEPS) - 1 else "green",
                                    leftSection=DashIconify(
                                        icon="tabler:arrow-right" if i < len(STEPS) - 1 else "tabler:check",
                                        width=16
                                    )
                                )
                            ], justify="flex-end")
                        ], gap="sm")
                    ], shadow="xs", padding="md", radius="md", bg="var(--mantine-color-violet-4)", mb="md")
                ]
            ) for i, step in enumerate(STEPS)
        ] + [
            dmc.StepperCompleted([
                dmc.Card([
                    dmc.Stack([
                        dmc.Center([
                            dmc.ThemeIcon(
                                DashIconify(icon="tabler:circle-check", width=40),
                                size=80,
                                radius="xl",
                                variant="light",
                                color="green"
                            )
                        ]),
                        dmc.Title("Setup Complete!", order=3, ta="center", c="green"),
                        dmc.Text(
                            "Your Star Micronics printer integration is ready to use.",
                            ta="center",
                            c="dimmed"
                        ),
                        dmc.Divider(),
                        dmc.SimpleGrid([
                            dmc.Card([
                                dmc.Stack([
                                    DashIconify(icon="tabler:book", width=24),
                                    dmc.Text("View Docs", size="sm", fw=500),
                                    dmc.Text("Read full documentation", size="xs", c="dimmed")
                                ], align="center")
                            ], padding="md", withBorder=True),
                            dmc.Card([
                                dmc.Stack([
                                    DashIconify(icon="tabler:printer", width=24),
                                    dmc.Text("Test Print", size="sm", fw=500),
                                    dmc.Text("Send a test receipt", size="xs", c="dimmed")
                                ], align="center")
                            ], padding="md", withBorder=True),
                            dmc.Card([
                                dmc.Stack([
                                    DashIconify(icon="tabler:dashboard", width=24),
                                    dmc.Text("Dashboard", size="sm", fw=500),
                                    dmc.Text("Open printer management", size="xs", c="dimmed")
                                ], align="center")
                            ], padding="md", withBorder=True)
                        ], cols=3, spacing="sm"),
                        dmc.Button(
                            "Start Over",
                            variant="light",
                            color="gray",
                            id="restart-btn",
                            fullWidth=True
                        )
                    ])
                ], shadow="sm", padding="lg", radius="md",)
            ])
        ]
    )
], size="md", px="md", py="xl")


@callback(
    [
        Output('setup-stepper', 'active'),
        Output('progress-bar', 'value'),
        Output('progress-badge', 'children')
    ],
    [
        Input({"type": "next-btn", "index": ALL}, 'n_clicks'),
        Input({"type": "prev-btn", "index": ALL}, 'n_clicks'),
        Input('restart-btn', 'n_clicks')
    ],
    State('setup-stepper', 'active'),
    prevent_initial_call=True
)
def handle_navigation(next_clicks, prev_clicks, restart_click, current_step):
    """Handle stepper navigation"""
    from dash import ctx

    if ctx.triggered_id == 'restart-btn':
        return 0, 0, "0%"

    if isinstance(ctx.triggered_id, dict):
        if ctx.triggered_id["type"] == "next-btn":
            new_step = min(current_step + 1, len(STEPS))
        else:  # prev-btn
            new_step = max(current_step - 1, 0)
    else:
        return current_step, current_step / len(STEPS) * 100, f"{int(current_step / len(STEPS) * 100)}%"

    progress = new_step / len(STEPS) * 100
    return new_step, progress, f"{int(progress)}%"
```


---

### Device Status Example

Monitor printer health with real-time status cards:

> **Note:** the examples on this page send toasts to `notification-container`,
> this documentation site's global `dmc.NotificationContainer`. If you copy an
> example into your own app, add
> `dmc.NotificationContainer(id="notification-container")` inside your
> `MantineProvider` (or retarget the `Output` to your own container id).



```python
# File: docs/dash_pos_printer/device_status_example.py

"""
Device Status Example - Star Micronics POS Printer
Displays real-time printer status with health diagnostics
"""

import dash_mantine_components as dmc
from dash import html, callback, Input, Output, no_update, ALL
from dash_iconify import DashIconify

# Mock device data for demonstration
MOCK_DEVICES = [
    {
        'Id': 'Kitchen-Printer-01',
        'AccessIdentifier': 'ABC123DEF456',
        'Mac': '00:11:62:XX:XX:XX',
        'ClientType': 'mC-Print3',
        'Status': {
            'Online': True,
            'PaperEmpty': False,
            'PaperLow': False,
            'CoverOpen': False,
            'MechanicalError': False,
            'CutterError': False,
            'HoldPrint': False
        },
        'QueuedJobs': 0,
        'LastConnection': 0,  # MQTT mode
        'PaperWidthMM': 72,
        'PollingInterval': 0
    },
    {
        'Id': 'Front-Desk-Printer',
        'AccessIdentifier': 'XYZ789GHI012',
        'Mac': '00:11:62:YY:YY:YY',
        'ClientType': 'TSP100IV',
        'Status': {
            'Online': True,
            'PaperEmpty': False,
            'PaperLow': True,  # Warning
            'CoverOpen': False,
            'MechanicalError': False,
            'CutterError': False,
            'HoldPrint': False
        },
        'QueuedJobs': 3,  # Some jobs pending
        'LastConnection': 4,  # HTTP polling
        'PaperWidthMM': 80,
        'PollingInterval': 3
    },
    {
        'Id': 'Bar-Printer',
        'AccessIdentifier': 'JKL345MNO678',
        'Mac': '00:11:62:ZZ:ZZ:ZZ',
        'ClientType': 'mC-Print2',
        'Status': {
            'Online': False,  # Offline
            'PaperEmpty': False,
            'PaperLow': False,
            'CoverOpen': False,
            'MechanicalError': False,
            'CutterError': False,
            'HoldPrint': False
        },
        'QueuedJobs': 0,
        'LastConnection': 325,  # Not seen in 5+ minutes
        'PaperWidthMM': 72,
        'PollingInterval': 5
    }
]


def analyze_device_status(device):
    """Analyze device status and return diagnostic info"""
    status = device.get('Status', {})
    issues = []
    warnings = []

    # Check critical issues
    if not status.get('Online', False):
        issues.append("Printer is offline")
    if status.get('PaperEmpty', False):
        issues.append("Paper is empty")
    if status.get('CoverOpen', False):
        issues.append("Cover is open")
    if status.get('MechanicalError', False):
        issues.append("Mechanical error detected")
    if status.get('CutterError', False):
        issues.append("Cutter error")

    # Check warnings
    if status.get('PaperLow', False):
        warnings.append("Paper is low")
    if device.get('QueuedJobs', 0) > 5:
        warnings.append(f"{device.get('QueuedJobs')} jobs in queue")
    if status.get('HoldPrint', False):
        warnings.append("Print is on hold")

    # Check connection
    last_conn = device.get('LastConnection', -1)
    if last_conn == 0:
        connection_mode = "MQTT"
    elif last_conn > 0:
        if last_conn > 120:
            warnings.append(f"Last seen {last_conn}s ago")
        connection_mode = "HTTP Polling"
    else:
        connection_mode = "Unknown"

    return {
        'issues': issues,
        'warnings': warnings,
        'connection_mode': connection_mode,
        'healthy': len(issues) == 0
    }


def create_device_card(device):
    """Create a status card for a single device"""
    analysis = analyze_device_status(device)

    # Determine overall status
    if not analysis['healthy']:
        status_color = "red"
        status_icon = "tabler:alert-circle"
        status_text = "Issues Detected"
    elif analysis['warnings']:
        status_color = "yellow"
        status_icon = "tabler:alert-triangle"
        status_text = "Warnings"
    else:
        status_color = "green"
        status_icon = "tabler:circle-check"
        status_text = "Healthy"

    # Build diagnostic messages
    diagnostic_items = []

    # Issues (red)
    for issue in analysis['issues']:
        diagnostic_items.append(
            dmc.Group([
                DashIconify(icon="tabler:x", width=16, color="#dc2626"),
                dmc.Text(issue, size="sm", c="red")
            ], gap="xs")
        )

    # Warnings (yellow)
    for warning in analysis['warnings']:
        diagnostic_items.append(
            dmc.Group([
                DashIconify(icon="tabler:alert-triangle", width=16, color="#eab308"),
                dmc.Text(warning, size="sm", c="yellow.8")
            ], gap="xs")
        )

    # If healthy
    if not diagnostic_items:
        diagnostic_items.append(
            dmc.Group([
                DashIconify(icon="tabler:check", width=16, color="#16a34a"),
                dmc.Text("All systems operational", size="sm", c="green")
            ], gap="xs")
        )

    return dmc.Card([
        dmc.Stack([
            # Device header with status
            dmc.Group([
                dmc.Group([
                    dmc.ThemeIcon(
                        DashIconify(icon=status_icon, width=24),
                        size=40,
                        radius="xl",
                        variant="light",
                        color=status_color
                    ),
                    dmc.Stack([
                        dmc.Text(device.get('Id', 'Unknown Device'), fw=600),
                        dmc.Group([
                            dmc.Badge(
                                status_text,
                                color=status_color,
                                variant="light",
                                size="sm"
                            ),
                            dmc.Badge(
                                analysis['connection_mode'],
                                color="blue",
                                variant="outline",
                                size="sm"
                            )
                        ], gap="xs")
                    ], gap=0)
                ])
            ], justify="space-between"),

            dmc.Divider(variant="dashed"),

            # Device info grid
            dmc.SimpleGrid([
                dmc.Stack([
                    dmc.Text("Access ID", size="xs", c="dimmed"),
                    dmc.Code(device.get('AccessIdentifier', 'N/A'), style={"fontSize": "11px"})
                ], gap=2),
                dmc.Stack([
                    dmc.Text("Model", size="xs", c="dimmed"),
                    dmc.Text(device.get('ClientType', 'Unknown'), size="sm", fw=500)
                ], gap=2),
                dmc.Stack([
                    dmc.Text("Queue", size="xs", c="dimmed"),
                    dmc.Badge(
                        f"{device.get('QueuedJobs', 0)} jobs",
                        color="blue" if device.get('QueuedJobs', 0) == 0 else "yellow",
                        variant="filled"
                    )
                ], gap=2),
                dmc.Stack([
                    dmc.Text("Paper", size="xs", c="dimmed"),
                    dmc.Text(
                        f"{device.get('PaperWidthMM', 'N/A')}mm",
                        size="sm",
                        fw=500
                    )
                ], gap=2),
            ], cols=2, spacing="sm", verticalSpacing="sm"),

            # Diagnostics section
            dmc.Paper(
                dmc.Stack(diagnostic_items, gap="xs"),
                p="sm",
                radius="sm",
                withBorder=True
            ),

            # Action buttons
            dmc.Group([
                dmc.Button(
                    "Test Print",
                    size="sm",
                    variant="light",
                    color="blue",
                    leftSection=DashIconify(icon="tabler:printer", width=16),
                    id={'type': 'test-print', 'index': device.get('AccessIdentifier', '')}
                ),
                dmc.Button(
                    "Clear Queue",
                    size="sm",
                    variant="subtle",
                    color="red",
                    leftSection=DashIconify(icon="tabler:trash", width=16),
                    id={'type': 'clear-queue', 'index': device.get('AccessIdentifier', '')},
                    disabled=device.get('QueuedJobs', 0) == 0
                )
            ], grow=True)
        ])
    ], shadow="sm", padding="lg", radius="md", withBorder=True,
        style={"borderColor": f"var(--mantine-color-{status_color}-2)"})


# Component layout
component = dmc.Container([
    # Header
    dmc.Stack([
        dmc.Group([
            dmc.ThemeIcon(
                DashIconify(icon="tabler:printer", width=30),
                size=40,
                radius="md",
                variant="light",
                color="blue"
            ),
            dmc.Title("Device Status Monitor", order=2)
        ]),
        dmc.Text(
            "Real-time printer health monitoring with diagnostics",
            c="dimmed",
            size="sm"
        )
    ], gap="xs", mb="lg"),

    # Summary badges
    dmc.Group([
        dmc.Badge(
            f"{len(MOCK_DEVICES)} Devices",
            size="lg",
            variant="filled",
            color="blue"
        ),
        dmc.Badge(
            f"{sum(1 for d in MOCK_DEVICES if d['Status']['Online'])} Online",
            size="lg",
            variant="filled",
            color="green"
        ),
        dmc.Badge(
            f"{sum(d.get('QueuedJobs', 0) for d in MOCK_DEVICES)} Queued Jobs",
            size="lg",
            variant="filled",
            color="yellow"
        ),
        dmc.Button(
            "Refresh",
            variant="light",
            color="blue",
            leftSection=DashIconify(icon="tabler:refresh", width=20),
            id="refresh-btn"
        )
    ], mb="lg"),

    # Device grid
    dmc.Grid([
        dmc.GridCol(
            create_device_card(device),
            span={"base": 12, "md": 6, "lg": 4}
        ) for device in MOCK_DEVICES
    ], gutter="md")
], size="xl", px="md", py="xl")


# Callbacks for button actions
@callback(
    Output('notification-container', 'sendNotifications'),
    Input('refresh-btn', 'n_clicks'),
    prevent_initial_call=True
)
def handle_refresh_device_status(n_clicks):
    """Handle refresh button click"""
    return [{
        "id": "refresh",
        "title": "Status Updated",
        "message": "All device statuses have been refreshed",
        "action": "show",
        "color": "blue",
        "icon": DashIconify(icon="tabler:refresh", width=20),
        "autoClose": 2000
    }]


@callback(
    Output('notification-container', 'sendNotifications', allow_duplicate=True),
    Input({'type': 'test-print', 'index': ALL}, 'n_clicks'),
    prevent_initial_call=True
)
def handle_test_print_device_status(n_clicks):
    """Handle test print button clicks"""
    if not any(n_clicks):
        return no_update

    return [{
        "id": "test-print",
        "title": "Test Print Sent",
        "message": "Receipt has been sent to the print queue",
        "action": "show",
        "color": "green",
        "icon": DashIconify(icon="tabler:printer-check", width=20),
        "autoClose": 3000
    }]


@callback(
    Output('notification-container', 'sendNotifications', allow_duplicate=True),
    Input({'type': 'clear-queue', 'index': ALL}, 'n_clicks'),
    prevent_initial_call=True
)
def handle_clear_queue_device_status(n_clicks):
    """Handle clear queue button clicks"""
    if not any(n_clicks):
        return no_update

    return [{
        "id": "clear-queue",
        "title": "Queue Cleared",
        "message": "All pending print jobs have been removed",
        "action": "show",
        "color": "orange",
        "icon": DashIconify(icon="tabler:trash-check", width=20),
        "autoClose": 2000
    }]
```


This example demonstrates:
- Real-time device status monitoring
- Health diagnostics with color-coded indicators
- Connection mode detection (HTTP vs MQTT)
- Paper level warnings
- Queue status tracking

---

### Print Receipt Example

Send formatted receipts to your printer:



```python
# File: docs/dash_pos_printer/print_receipt_example.py

"""
Print Receipt Example - Star Micronics POS Printer
Interactive receipt printing with Star Document Markup
"""

import dash_mantine_components as dmc
from dash import html, callback, Input, Output, State, no_update
from dash_iconify import DashIconify
from datetime import datetime

# Sample receipt templates
RECEIPT_TEMPLATES = {
    "simple": """[align: center]
[bold][mag: w 2; h 2]YOUR STORE NAME[normal][mag: w 1; h 1]
================================

[align: left]
Order #: ORD-12345
Date: {date}
Time: {time}

================================
[bold]ITEMS:[normal]
2x Lobster Roll         $35.98
1x Clam Chowder         $12.99
--------------------------------
Subtotal:               $48.97
Tax (8.25%):            $4.04
================================
[bold]TOTAL:                  $52.01[normal]

Payment: Credit Card

[align: center]
Thank you for your order!
Visit us again soon!

[cut]""",

    "detailed": """[align: center]
[bold][mag: w 2; h 2]RESTAURANT NAME[normal][mag: w 1; h 1]
123 Main Street
City, State 12345
Phone: (555) 123-4567
================================

[align: left]
Order #: ORD-12345
Server: John Doe
Table: 5
Date: {date}
Time: {time}

================================
[bold]ITEMS:[normal]
Qty  Item           Price  Total
--------------------------------
2    Lobster Roll   17.99  35.98
     - Extra Mayo
1    Clam Chowder   12.99  12.99
     - Bread Bowl
1    Iced Tea        2.99   2.99
--------------------------------
Subtotal:                  $51.96
Tax (8.25%):               $4.29
Tip (18%):                 $9.35
================================
[bold]TOTAL:                     $65.60[normal]

Payment: Credit Card ****1234
Auth Code: 123456

[align: center]
★★★ Thank You! ★★★
Please visit us again soon!

Rate your experience:
www.yourrestaurant.com/review

[cut]""",

    "minimal": """[align: center]
QUICK RECEIPT

[align: left]
Order: #{order_num}
Items: 3
Total: $52.01

{date} {time}

[align: center]
Thank you!
[cut]"""
}

# Component layout
component = dmc.Container([
        # Header
        dmc.Stack([
            dmc.Group([
                dmc.ThemeIcon(
                    DashIconify(icon="tabler:receipt", width=30),
                    size=40,
                    radius="md",
                    variant="light",
                    color="blue"
                ),
                dmc.Title("Print Receipt", order=2)
            ]),
            dmc.Text(
                "Create and send formatted receipts using Star Document Markup",
                c="dimmed",
                size="sm"
            )
        ], gap="xs", mb="lg"),

        dmc.Grid([
            # Left column - Template selection and preview
            dmc.GridCol([
                dmc.Card([
                    dmc.Stack([
                        dmc.Title("Receipt Template", order=3, c="dimmed", size="h4"),

                        # Template selector
                        dmc.SegmentedControl(
                            id="template-selector",
                            data=[
                                {"value": "simple", "label": "Simple"},
                                {"value": "detailed", "label": "Detailed"},
                                {"value": "minimal", "label": "Minimal"},
                            ],
                            value="simple",
                            fullWidth=True,
                            color="blue"
                        ),

                        dmc.Divider(),

                        # Preview
                        dmc.Stack([
                            dmc.Group([
                                DashIconify(icon="tabler:eye", width=16),
                                dmc.Text("Preview", size="sm", fw=500)
                            ]),
                            dmc.ScrollArea(
                                dmc.Code(
                                    id="receipt-preview",
                                    block=True,
                                    style={
                                        "whiteSpace": "pre",
                                        "fontFamily": "monospace",
                                        "fontSize": "12px",
                                        "lineHeight": "1.4"
                                    }
                                ),
                                h=400,
                                style={
                                    "border": "1px solid var(--mantine-color-gray-3)",
                                    "borderRadius": "var(--mantine-radius-sm)"
                                }
                            )
                        ])
                    ])
                ], shadow="sm", padding="lg", radius="md", withBorder=True)
            ], span={"base": 12, "md": 6}),

            # Right column - Print options and controls
            dmc.GridCol([
                dmc.Card([
                    dmc.Stack([
                        dmc.Title("Print Options", order=3, c="dimmed", size="h4"),

                        # Device selector
                        dmc.Select(
                            id="device-select",
                            label="Target Printer",
                            placeholder="Select a printer",
                            data=[
                                {"value": "ABC123", "label": "🟢 Kitchen Printer (ABC123)"},
                                {"value": "XYZ789", "label": "🟢 Front Desk (XYZ789)"},
                                {"value": "JKL345", "label": "🔴 Bar Printer (JKL345) - Offline"},
                            ],
                            value="ABC123",
                            leftSection=DashIconify(icon="tabler:printer", width=16)
                        ),

                        # Print settings
                        dmc.NumberInput(
                            id="copies-input",
                            label="Number of Copies",
                            description="Print 1-10 copies",
                            value=1,
                            min=1,
                            max=10,
                            step=1,
                            leftSection=DashIconify(icon="tabler:copy", width=16)
                        ),

                        dmc.Group([
                            dmc.NumberInput(
                                id="buzzer-before",
                                label="Buzzer Before",
                                description="Beeps before printing",
                                value=0,
                                min=0,
                                max=3,
                                step=1,
                                style={"flex": 1}
                            ),
                            dmc.NumberInput(
                                id="buzzer-after",
                                label="Buzzer After",
                                description="Beeps after printing",
                                value=1,
                                min=0,
                                max=3,
                                step=1,
                                style={"flex": 1}
                            )
                        ]),

                        dmc.Divider(),

                        # Action buttons
                        dmc.Stack([
                            dmc.Button(
                                "Send to Printer",
                                id="print-btn",
                                color="blue",
                                leftSection=DashIconify(icon="tabler:send", width=20),
                                fullWidth=True,
                                size="lg"
                            ),
                            dmc.Button(
                                "Test Print",
                                id="test-print-btn",
                                variant="light",
                                color="blue",
                                leftSection=DashIconify(icon="tabler:printer", width=20),
                                fullWidth=True
                            )
                        ]),

                        # Info alert
                        dmc.Alert(
                            [
                                dmc.Stack([
                                    dmc.Text("Star Document Markup", size="sm", fw=600),
                                    dmc.Text(
                                        "Use markup commands like [bold], [align: center], and [mag: w 2; h 2] to format your receipts.",
                                        size="xs"
                                    )
                                ])
                            ],
                            title="Formatting Guide",
                            icon=DashIconify(icon="tabler:info-circle", width=20),
                            color="blue",
                            variant="light"
                        )
                    ])
                ], shadow="sm", padding="lg", radius="md", withBorder=True)
            ], span={"base": 12, "md": 6})
        ], gutter="md")
], size="xl", px="md", py="xl")


@callback(
    Output('receipt-preview', 'children'),
    Input('template-selector', 'value')
)
def update_preview(template_key):
    """Update receipt preview based on selected template"""
    now = datetime.now()
    receipt = RECEIPT_TEMPLATES.get(template_key, RECEIPT_TEMPLATES['simple'])

    # Fill in dynamic values
    receipt = receipt.format(
        date=now.strftime('%m/%d/%Y'),
        time=now.strftime('%I:%M %p'),
        order_num="12345"
    )

    return receipt


@callback(
    Output('notification-container', 'sendNotifications', allow_duplicate=True),
    Input('print-btn', 'n_clicks'),
    Input('test-print-btn', 'n_clicks'),
    State('template-selector', 'value'),
    State('device-select', 'value'),
    State('copies-input', 'value'),
    State('buzzer-before', 'value'),
    State('buzzer-after', 'value'),
    prevent_initial_call=True
)
def handle_print_receipt(print_clicks, test_clicks, template, device, copies, buzzer_before, buzzer_after):
    """Handle print and test print button clicks"""
    from dash import ctx

    if ctx.triggered_id == 'print-btn':
        return [{
            "id": "print-success",
            "title": "Receipt Sent",
            "message": f"Print job sent to {device} ({copies} copies)",
            "action": "show",
            "color": "green",
            "icon": DashIconify(icon="tabler:printer-check", width=20),
            "autoClose": 3000
        }]
    elif ctx.triggered_id == 'test-print-btn':
        return [{
            "id": "test-print",
            "title": "Test Print Sent",
            "message": f"Test receipt sent to {device}",
            "action": "show",
            "color": "blue",
            "icon": DashIconify(icon="tabler:printer", width=20),
            "autoClose": 2000
        }]

    return no_update
```


Features:
- Star Document Markup formatting
- Text alignment (left, center, right)
- Bold and sized text
- Automatic paper cutting
- Buzzer alerts

---

### Star Document Markup

The system uses Star Document Markup for formatting receipts:

#### Text Formatting

```python
# Alignment
"[align: left]Left aligned text"
"[align: center]Centered text"
"[align: right]Right aligned text"

# Bold text
"[bold]Bold text[normal]"

# Text sizing
"[mag: w 2; h 1]Double width"
"[mag: w 1; h 2]Double height"
"[mag: w 2; h 2]Double both"

# Paper cut (required at end)
"[cut]"
```

#### Example Receipt

```python
receipt = """[align: center]
[bold][mag: w 2; h 2]YOUR STORE[normal][mag: w 1; h 1]
================================

[align: left]
Order #: ORD-12345
Customer: John Doe
Date: 11/17/2025

================================
[bold]ITEMS:[normal]
2x Lobster Roll         $35.98
1x Clam Chowder         $12.99
--------------------------------
Subtotal:               $48.97
Tax (8.25%):            $4.04
================================
[bold]TOTAL:                  $52.01[normal]

Payment: Credit Card

[align: center]
Thank you for your order!
Visit us again soon!

[cut]"""
```

---

### Printer Management Dashboard

Full-featured admin dashboard for printer control:



```python
# File: docs/dash_pos_printer/printer_dashboard_example.py

"""
Printer Dashboard Example - Star Micronics POS Printer
Mini dashboard with tabbed interface for printer management
"""

import dash_mantine_components as dmc
from dash import html, callback, Input, Output, no_update
from dash_iconify import DashIconify

# Component layout
component = dmc.Container([
        # Header
        dmc.Stack([
            dmc.Group([
                dmc.ThemeIcon(
                    DashIconify(icon="tabler:layout-dashboard", width=30),
                    size=40,
                    radius="md",
                    variant="light",
                    color="blue"
                ),
                dmc.Title("Printer Management Dashboard", order=2)
            ]),
            dmc.Text(
                "Comprehensive printer control and monitoring interface",
                c="dimmed",
                size="sm"
            )
        ], gap="xs", mb="lg"),

        # Status summary
        dmc.Group([
            dmc.Badge("3 Devices", size="lg", variant="filled", color="blue"),
            dmc.Badge("2 Online", size="lg", variant="filled", color="green"),
            dmc.Badge("3 Queued Jobs", size="lg", variant="filled", color="yellow"),
            dmc.Button(
                "Refresh All",
                variant="light",
                color="blue",
                leftSection=DashIconify(icon="tabler:refresh", width=20),
                id="refresh-all"
            )
        ], mb="lg"),

        # Tabbed interface
        dmc.Tabs([
            dmc.TabsList([
                dmc.TabsTab(
                    "Overview",
                    value="overview",
                    leftSection=DashIconify(icon="tabler:layout", width=16)
                ),
                dmc.TabsTab(
                    "Print Queue",
                    value="queue",
                    leftSection=DashIconify(icon="tabler:list", width=16)
                ),
                dmc.TabsTab(
                    "Quick Print",
                    value="print",
                    leftSection=DashIconify(icon="tabler:printer", width=16)
                ),
                dmc.TabsTab(
                    "History",
                    value="history",
                    leftSection=DashIconify(icon="tabler:history", width=16)
                )
            ]),

            # Overview Tab
            dmc.TabsPanel(value="overview", children=[
                dmc.SimpleGrid([
                    # Printer 1 - Healthy
                    dmc.Card([
                        dmc.Stack([
                            dmc.Group([
                                dmc.ThemeIcon(
                                    DashIconify(icon="tabler:circle-check", width=20),
                                    color="green",
                                    variant="light",
                                    size="lg"
                                ),
                                dmc.Stack([
                                    dmc.Text("Kitchen Printer", fw=600),
                                    dmc.Badge("Healthy", color="green", size="sm")
                                ], gap=0)
                            ]),
                            dmc.Text("mC-Print3 • MQTT • 0 jobs", size="sm", c="dimmed"),
                            dmc.Button(
                                "Test Print",
                                size="sm",
                                variant="light",
                                fullWidth=True,
                                leftSection=DashIconify(icon="tabler:printer", width=16)
                            )
                        ])
                    ], shadow="sm", padding="md", radius="md", withBorder=True),

                    # Printer 2 - Warning
                    dmc.Card([
                        dmc.Stack([
                            dmc.Group([
                                dmc.ThemeIcon(
                                    DashIconify(icon="tabler:alert-triangle", width=20),
                                    color="yellow",
                                    variant="light",
                                    size="lg"
                                ),
                                dmc.Stack([
                                    dmc.Text("Front Desk", fw=600),
                                    dmc.Badge("Paper Low", color="yellow", size="sm")
                                ], gap=0)
                            ]),
                            dmc.Text("TSP100IV • HTTP • 3 jobs", size="sm", c="dimmed"),
                            dmc.Button(
                                "Clear Queue",
                                size="sm",
                                variant="light",
                                color="orange",
                                fullWidth=True,
                                leftSection=DashIconify(icon="tabler:trash", width=16)
                            )
                        ])
                    ], shadow="sm", padding="md", radius="md", withBorder=True),

                    # Printer 3 - Offline
                    dmc.Card([
                        dmc.Stack([
                            dmc.Group([
                                dmc.ThemeIcon(
                                    DashIconify(icon="tabler:alert-circle", width=20),
                                    color="red",
                                    variant="light",
                                    size="lg"
                                ),
                                dmc.Stack([
                                    dmc.Text("Bar Printer", fw=600),
                                    dmc.Badge("Offline", color="red", size="sm")
                                ], gap=0)
                            ]),
                            dmc.Text("mC-Print2 • Last seen 5m ago", size="sm", c="dimmed"),
                            dmc.Button(
                                "Diagnose",
                                size="sm",
                                variant="light",
                                color="red",
                                fullWidth=True,
                                leftSection=DashIconify(icon="tabler:stethoscope", width=16)
                            )
                        ])
                    ], shadow="sm", padding="md", radius="md", withBorder=True)
                ], cols={"base": 1, "sm": 2, "lg": 3}, spacing="md", mt="md")
            ]),

            # Print Queue Tab
            dmc.TabsPanel(value="queue", children=[
                dmc.Card([
                    dmc.Stack([
                        dmc.Title("Active Print Queue", order=4),
                        dmc.Text("3 jobs pending across all devices", size="sm", c="dimmed"),
                        dmc.Divider(),

                        # Queue items
                        dmc.Paper([
                            dmc.Group([
                                dmc.Stack([
                                    dmc.Text("Order #12345", fw=500),
                                    dmc.Text("Front Desk Printer", size="xs", c="dimmed")
                                ], gap=0),
                                dmc.Badge("Pending", color="yellow")
                            ], justify="space-between")
                        ], p="sm", withBorder=True, mb="xs"),

                        dmc.Paper([
                            dmc.Group([
                                dmc.Stack([
                                    dmc.Text("Order #12346", fw=500),
                                    dmc.Text("Front Desk Printer", size="xs", c="dimmed")
                                ], gap=0),
                                dmc.Badge("Pending", color="yellow")
                            ], justify="space-between")
                        ], p="sm", withBorder=True, mb="xs"),

                        dmc.Paper([
                            dmc.Group([
                                dmc.Stack([
                                    dmc.Text("Test Print", fw=500),
                                    dmc.Text("Front Desk Printer", size="xs", c="dimmed")
                                ], gap=0),
                                dmc.Badge("Pending", color="yellow")
                            ], justify="space-between")
                        ], p="sm", withBorder=True, mb="md"),

                        dmc.Button(
                            "Clear All Queues",
                            color="red",
                            variant="outline",
                            fullWidth=True,
                            leftSection=DashIconify(icon="tabler:trash", width=16)
                        )
                    ])
                ], shadow="sm", padding="lg", radius="md", withBorder=True, mt="md")
            ]),

            # Quick Print Tab
            dmc.TabsPanel(value="print", children=[
                dmc.Card([
                    dmc.Stack([
                        dmc.Title("Print Templates", order=4),
                        dmc.SimpleGrid([
                            dmc.Button(
                                "Test Receipt",
                                color="blue",
                                variant="light",
                                h=60,
                                leftSection=DashIconify(icon="tabler:receipt", width=20)
                            ),
                            dmc.Button(
                                "Sample Order",
                                color="blue",
                                variant="light",
                                h=60,
                                leftSection=DashIconify(icon="tabler:shopping-cart", width=20)
                            ),
                            dmc.Button(
                                "Test Pattern",
                                color="blue",
                                variant="light",
                                h=60,
                                leftSection=DashIconify(icon="tabler:grid-pattern", width=20)
                            ),
                            dmc.Button(
                                "Buzzer Test",
                                color="blue",
                                variant="light",
                                h=60,
                                leftSection=DashIconify(icon="tabler:bell", width=20)
                            )
                        ], cols=2, spacing="sm")
                    ])
                ], shadow="sm", padding="lg", radius="md", withBorder=True, mt="md")
            ]),

            # History Tab
            dmc.TabsPanel(value="history", children=[
                dmc.Card([
                    dmc.Stack([
                        dmc.Group([
                            dmc.Title("Print History", order=4),
                            dmc.Button(
                                "Clear",
                                size="sm",
                                variant="subtle",
                                color="red",
                                leftSection=DashIconify(icon="tabler:trash", width=16)
                            )
                        ], justify="space-between"),

                        # History items
                        dmc.Timeline([
                            dmc.TimelineItem(
                                [
                                    dmc.Text("Order #12347 printed", fw=500, size="sm"),
                                    dmc.Text("Kitchen Printer • 2 minutes ago", size="xs", c="dimmed")
                                ],
                                bullet=DashIconify(icon="tabler:check", width=12, color="green"),
                                # bulletSize=24
                            ),
                            dmc.TimelineItem(
                                [
                                    dmc.Text("Test Print sent", fw=500, size="sm"),
                                    dmc.Text("Front Desk • 5 minutes ago", size="xs", c="dimmed")
                                ],
                                bullet=DashIconify(icon="tabler:check", width=12, color="green"),
                                # bulletSize=24
                            ),
                            dmc.TimelineItem(
                                [
                                    dmc.Text("Order #12346 failed", fw=500, size="sm"),
                                    dmc.Text("Bar Printer • 10 minutes ago", size="xs", c="dimmed")
                                ],
                                bullet=DashIconify(icon="tabler:x", width=12, color="red"),
                                # bulletSize=24
                            )
                        ])
                    ])
                ], shadow="sm", padding="lg", radius="md", withBorder=True, mt="md")
            ])
        ], value="overview")
], size="xl", px="md", py="xl")


@callback(
    Output('notification-container', 'sendNotifications', allow_duplicate=True),
    Input('refresh-all', 'n_clicks'),
    prevent_initial_call=True
)
def handle_refresh_dashboard(n_clicks):
    return [{
        "id": "refresh",
        "title": "Dashboard Refreshed",
        "message": "All printer statuses updated",
        "action": "show",
        "color": "blue",
        "icon": DashIconify(icon="tabler:refresh", width=20),
        "autoClose": 2000
    }]
```


Dashboard Features:
- **Device Overview** - Real-time status for all printers
- **Print Queue** - View and manage pending jobs
- **Quick Print** - Template prints and custom jobs
- **Diagnostics** - System health analysis
- **Print History** - Last 20 print jobs with status

---

### StarPrinterService API

#### Initialization

```python
from printer_service import StarPrinterService

# Initialize (reads from .env)
printer = StarPrinterService()
```

### Device Management

```python
# List all devices
devices = printer.get_device_list()

for device in devices:
    print(f"Device: {device['Id']}")
    print(f"Online: {device['Status']['Online']}")
    print(f"Queue: {device['QueuedJobs']} jobs")

# Get specific device status
status = printer.get_device_status('ABC123DEF456')
```

#### Print Operations

```python
# Basic print
result = printer.print_receipt(
    content="[align: center]Hello World\n[cut]",
    job_name="Test_Print",
    device_id="ABC123DEF456"
)

# Print with buzzer
result = printer.print_receipt(
    content=receipt_content,
    job_name="Order_12345",
    device_id="ABC123DEF456",
    startbuzzer=2,  # Buzz twice before
    endbuzzer=1     # Buzz once after
)

# Print multiple copies
result = printer.print_receipt(
    content=receipt_content,
    copies=3,
    device_id="ABC123DEF456"
)

# Check result
if 'JobId' in result:
    print(f"✅ Success! Job ID: {result['JobId']}")
else:
    print(f"❌ Error: {result.get('error')}")
```

#### Queue Management

```python
# Clear all pending jobs
success = printer.clear_queue('ABC123DEF456')

if success:
    print("✅ Queue cleared successfully")
```

#### Generate Receipt from Order

```python
order_data = {
    'order_number': 'ORD-12345',
    'customer_name': 'John Doe',
    'customer_phone': '(555) 123-4567',
    'items': [
        {'name': 'Lobster Roll', 'quantity': 2, 'amount': 35.98},
        {'name': 'Clam Chowder', 'quantity': 1, 'amount': 12.99}
    ],
    'amount': 48.97,
    'payment_method': 'Credit Card'
}

# Generate formatted receipt
receipt_markup = printer.generate_receipt_markup(order_data)

# Print it
printer.print_receipt(receipt_markup, job_name=f"Order_{order_data['order_number']}")
```

---

### Automatic Order Printing

Integrate with your checkout process to automatically print receipts:

```python
from printer_service import StarPrinterService
import stripe

def on_checkout_success(session_id):
    """Print receipt when customer completes checkout"""

    # Retrieve order from Stripe
    session = stripe.checkout.Session.retrieve(
        session_id,
        expand=['line_items', 'line_items.data.price.product']
    )

    # Format order data
    order_data = {
        'order_number': session.id[:12].upper(),
        'customer_name': session.customer_details.name,
        'customer_phone': session.customer_details.phone,
        'items': format_line_items(session.line_items),
        'amount': session.amount_total / 100,
        'payment_method': 'Credit Card'
    }

    # Print receipt
    printer = StarPrinterService()
    receipt = printer.generate_receipt_markup(order_data)

    result = printer.print_receipt(
        receipt,
        job_name=f"Order_{order_data['order_number']}",
        startbuzzer=2  # Alert staff with 2 beeps
    )

    if 'JobId' in result:
        print(f"✅ Receipt printed! Job ID: {result['JobId']}")
    else:
        print(f"⚠️ Print failed: {result.get('error')}")
        # Queue for retry or alert admin
```

---

### Troubleshooting

#### Printer Shows Offline

**Symptoms:**
- Device appears offline in dashboard
- Status shows "Printer is offline"

**Solutions:**
```bash
# Check network connection
ping [PRINTER_IP]

# Verify CloudPRNT URL
# Should be: https://api.stario.online/v1/a/[GROUP_PATH]/cloudprnt

# Check StarIO.Online dashboard
# Device should appear under Device Groups

# Restart printer
# Power cycle and wait 30 seconds
```

#### Jobs Stuck in Queue

**Symptoms:**
- QueuedJobs count > 0
- Jobs not printing
- LastConnection timestamp increasing

**Solutions:**
```python
# Check connection mode
status = printer.get_device_status(device_id)
last_conn = status['LastConnection']

if last_conn == 0:
    print("MQTT mode - check firmware version")
    print("Required: mC-Print3 5.1+ or TSP100IV 2.2+")
else:
    print(f"HTTP polling - last seen {last_conn}s ago")
    if last_conn > 60:
        print("Printer not polling - verify CloudPRNT URL")

# Clear stuck queue
printer.clear_queue(device_id)
```

#### Authentication Errors

**Symptoms:**
- "API key not found" error
- 401 Unauthorized responses

**Solutions:**
```bash
# Verify .env file exists and is correct
cat .env | grep STAR_MICRONICS

# Ensure no spaces in values
STAR_MICRONICS=your_key_here  # Correct
STAR_MICRONICS= your_key_here  # Wrong (has space)

# Check API key permissions in StarIO.Online dashboard
# Required: PrintToDevice, ViewDeviceGroups, ViewDevice, FlushQueue

# Test connection
python printer_service.py
```

---

### Component Properties

#### StarPrinterService Class

| Property | Type | Default | Description |
|:---------|:-----|:--------|:------------|
| `api_key` | string | env: STAR_MICRONICS | Star Micronics API key for authentication |
| `group_path` | string | env: STAR_GROUP_PATH | Device group path from StarIO.Online |
| `device_id` | string | env: STAR_DEVICE_ID | Optional default device identifier |
| `base_url` | string | https://api.stario.online/v1 | API base URL (US region) |

#### Method: print_receipt()

| Parameter | Type | Default | Description |
|:----------|:-----|:--------|:------------|
| `content` | string | Required | Star Markup formatted receipt content |
| `job_name` | string | None | Name for job tracking in dashboard |
| `device_id` | string | None | Target device (uses default if not set) |
| `copies` | integer | 1 | Number of copies to print (1-10) |
| `startbuzzer` | integer | 0 | Number of beeps before printing (0-3) |
| `endbuzzer` | integer | 0 | Number of beeps after printing (0-3) |

#### Device Status Object

| Property | Type | Description |
|:---------|:-----|:------------|
| `Id` | string | Device name/identifier |
| `AccessIdentifier` | string | Unique device ID for API calls |
| `Mac` | string | MAC address of printer |
| `ClientType` | string | Printer model (e.g., mC-Print3) |
| `Status.Online` | boolean | Whether printer is connected |
| `Status.PaperEmpty` | boolean | Paper roll is empty |
| `Status.PaperLow` | boolean | Paper roll is running low |
| `Status.CoverOpen` | boolean | Printer cover is open |
| `QueuedJobs` | integer | Number of pending print jobs |
| `LastConnection` | integer | Seconds since last poll (0 for MQTT) |
| `PaperWidthMM` | integer | Paper width in millimeters (72 or 80) |
| `PollingInterval` | integer | Polling interval in seconds |

---

### Best Practices

#### 1. Error Handling

Always wrap printer calls in try-except blocks:

```python
def safe_print(content, device_id):
    try:
        printer = StarPrinterService()
        result = printer.print_receipt(content, device_id=device_id)

        if 'error' in result:
            # Log error but don't fail checkout
            logger.error(f"Print error: {result['error']}")
            # Maybe queue for retry
            return False

        return True

    except Exception as e:
        logger.exception(f"Printer exception: {e}")
        # Alert admin but continue
        return False
```

#### 2. Queue Monitoring

Monitor print queues and alert on issues:

```python
def check_queue_health():
    """Run periodically to monitor queue health"""
    printer = StarPrinterService()
    devices = printer.get_device_list()

    for device in devices:
        queue_count = device.get('QueuedJobs', 0)

        if queue_count > 10:
            # Alert admin
            send_alert(f"Printer {device['Id']} has {queue_count} pending jobs")

        if queue_count > 50:
            # Auto-clear very old jobs
            printer.clear_queue(device['AccessIdentifier'])
```

#### 3. Receipt Design

Design for thermal printer constraints:

```python
# GOOD: Clear, readable, proper width
receipt = """[align: center]
[bold][mag: w 2; h 2]RESTAURANT NAME[normal][mag: w 1; h 1]

[align: left]
Order: 12345
2x Item Name      $35.98
1x Another Item   $12.99
--------------------------
TOTAL:            $48.97

[align: center]
Thank you!
[cut]"""

# BAD: Too wide, will wrap or cut off
# Lines should not exceed 32 chars for 72mm paper
# or 48 chars for 80mm paper
```

#### 4. Performance Optimization

Cache device status to reduce API calls:

```python
import time

class CachedPrinterService:
    def __init__(self):
        self.printer = StarPrinterService()
        self._devices_cache = None
        self._cache_time = 0
        self._cache_ttl = 30  # 30 seconds

    def get_device_list(self):
        now = time.time()
        if self._devices_cache is None or (now - self._cache_time) > self._cache_ttl:
            self._devices_cache = self.printer.get_device_list()
            self._cache_time = now
        return self._devices_cache
```

---

### Live Printer Test

Send custom messages directly to your actual Star Micronics printer:



```python
# File: docs/dash_pos_printer/live_print_example.py

"""
Live Print Example - Star Micronics POS Printer
Send a message directly to the creator of this documentation via their printer
"""

import os
import dash_mantine_components as dmc
import dash_ag_grid as dag
from dash import html, callback, Input, Output, State, no_update, dcc
from dash_iconify import DashIconify
from datetime import datetime

# Import printer service module but don't instantiate yet
printer_service = None
PRINTER_SERVICE_AVAILABLE = False
PRINTER_IMPORT_ERROR = None

try:
    # Import from support_files
    import sys
    sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../..'))
    from support_files.printer_service import StarPrinterService
    PRINTER_SERVICE_AVAILABLE = True
except Exception as e:
    PRINTER_IMPORT_ERROR = str(e)
    print(f"Warning: Printer service module not available: {e}")
    StarPrinterService = None

# Get device ID from environment
STAR_API_KEY = os.environ.get('STAR_MICRONICS')
DEVICE_ID = os.environ.get('STAR_DEVICE_ID')
GROUP_PATH = os.environ.get('STAR_GROUP_PATH', 'pipinstallpython')
REGION = os.environ.get('STAR_REGION', 'US')

# Diagnostic logging on startup
print("\n" + "="*60)
print("PRINTER SERVICE ENVIRONMENT CHECK")
print("="*60)
print(f"STAR_MICRONICS API Key: {'✅ SET' if STAR_API_KEY else '❌ NOT SET'}")
print(f"STAR_DEVICE_ID: {'✅ ' + DEVICE_ID if DEVICE_ID else '❌ NOT SET'}")
print(f"STAR_GROUP_PATH: {GROUP_PATH}")
print(f"STAR_REGION: {REGION}")
print(f"Service Import: {'✅ SUCCESS' if PRINTER_SERVICE_AVAILABLE else '❌ FAILED: ' + str(PRINTER_IMPORT_ERROR)}")
print("="*60 + "\n")

PRINTER_AVAILABLE = PRINTER_SERVICE_AVAILABLE and DEVICE_ID is not None and STAR_API_KEY is not None
MISSING_ENV_VARS = []
if not STAR_API_KEY:
    MISSING_ENV_VARS.append('STAR_MICRONICS')
if not DEVICE_ID:
    MISSING_ENV_VARS.append('STAR_DEVICE_ID')


def get_printer_service():
    """Lazy initialization of printer service - only create when needed"""
    global printer_service
    if not PRINTER_SERVICE_AVAILABLE:
        return None

    if printer_service is None:
        try:
            printer_service = StarPrinterService()
        except Exception as e:
            print(f"Error initializing printer service: {e}")
            return None

    return printer_service

# Component layout
component = dmc.Container([
    # Interval for queue updates (disabled by default to prevent auto-refresh)
    dcc.Interval(id='queue-refresh-interval', interval=5000, disabled=True, n_intervals=0),

    # Header
    dmc.Stack([
        dmc.Group([
            dmc.ThemeIcon(
                DashIconify(icon="tabler:send", width=30),
                size=40,
                radius="md",
                variant="light",
                color="blue" if PRINTER_AVAILABLE else "red"
            ),
            dmc.Title("Message the Creator", order=2)
        ]),
        dmc.Text(
            "Send a message directly to the creator of this documentation" if PRINTER_AVAILABLE else "Printer not configured - check environment variables",
            c="dimmed" if PRINTER_AVAILABLE else "red",
            size="sm"
        )
    ], gap="xs", mb="lg"),

    # Status indicator with detailed environment variable diagnostics
    dmc.Card([
        dmc.Stack([
            # Overall status
            dmc.Group([
                DashIconify(
                    icon="tabler:circle-check" if PRINTER_AVAILABLE else "tabler:alert-circle",
                    width=20,
                    color="#16a34a" if PRINTER_AVAILABLE else "#dc2626"
                ),
                dmc.Text(
                    "Printer Connected" if PRINTER_AVAILABLE else "Printer Not Available",
                    fw=600,
                    c="green" if PRINTER_AVAILABLE else "red"
                )
            ]),

            dmc.Divider(variant="dashed"),

            # Environment variable status
            dmc.Stack([
                dmc.Text("Environment Variables", size="xs", fw=600, c="dimmed", tt="uppercase"),
                dmc.Group([
                    DashIconify(
                        icon="tabler:check" if STAR_API_KEY else "tabler:x",
                        width=14,
                        color="#16a34a" if STAR_API_KEY else "#dc2626"
                    ),
                    dmc.Text(
                        f"STAR_MICRONICS: {'Set (***{STAR_API_KEY[-4:]})' if STAR_API_KEY else '❌ NOT SET'}",
                        size="sm",
                        c="dimmed" if STAR_API_KEY else "red"
                    )
                ], gap="xs"),
                dmc.Group([
                    DashIconify(
                        icon="tabler:check" if DEVICE_ID else "tabler:x",
                        width=14,
                        color="#16a34a" if DEVICE_ID else "#dc2626"
                    ),
                    dmc.Text(
                        f"STAR_DEVICE_ID: {DEVICE_ID if DEVICE_ID else '❌ NOT SET'}",
                        size="sm",
                        c="dimmed" if DEVICE_ID else "red"
                    )
                ], gap="xs"),
                dmc.Group([
                    DashIconify(icon="tabler:check", width=14, color="#16a34a"),
                    dmc.Text(f"STAR_GROUP_PATH: {GROUP_PATH}", size="sm", c="dimmed")
                ], gap="xs"),
                dmc.Group([
                    DashIconify(icon="tabler:check", width=14, color="#16a34a"),
                    dmc.Text(f"STAR_REGION: {REGION}", size="sm", c="dimmed")
                ], gap="xs"),
            ], gap="xs"),

            # Missing variables alert
            dmc.Alert(
                [
                    dmc.Stack([
                        dmc.Text("Missing Environment Variables", size="sm", fw=600),
                        dmc.Text(
                            f"Please configure: {', '.join(MISSING_ENV_VARS)}",
                            size="xs"
                        ),
                        dmc.Text(
                            "On Render: Dashboard → Environment → Add Variable",
                            size="xs",
                            c="dimmed"
                        )
                    ], gap="xs")
                ],
                title="Configuration Required",
                icon=DashIconify(icon="tabler:alert-triangle", width=20),
                color="red",
                variant="light"
            ) if MISSING_ENV_VARS else None,
        ], gap="sm")
    ], shadow="sm", padding="md", radius="md", withBorder=True, mb="lg"),

    # Message composer
    dmc.Grid([
        # Left column - Message input
        dmc.GridCol([
            dmc.Card([
                dmc.Stack([
                    dmc.Title("Compose Message", order=3, c="dimmed", size="h4"),

                    # Message template selector
                    dmc.Select(
                        id="message-template",
                        label="Message Type",
                        placeholder="Custom message",
                        data=[
                            {"value": "custom", "label": "Custom Message"},
                        ],
                        value="custom",
                        leftSection=DashIconify(icon="tabler:message", width=16),
                        disabled=True
                    ),

                    dmc.Divider(),

                    # Message editor
                    dmc.Textarea(
                        id="message-content",
                        label="Your Message",
                        description="Write your message to the creator - supports formatting like [bold], [align: center]",
                        placeholder="Write your message to the creator...",
                        minRows=10,
                        maxRows=15,
                        autosize=True,
                        value="[align: center]\n[bold][mag: w 2; h 2]Hello![normal][mag: w 1; h 1]\n\n[align: left]\nGreat documentation!\n\nI'm exploring dash-pos-printer and wanted to say hi.\n\n[align: center]\n{date} • {time}\n\n[cut]",
                    ),

                    # Print settings
                    dmc.Group([
                        dmc.NumberInput(
                            id="live-copies",
                            label="Copies",
                            value=1,
                            min=1,
                            max=10,
                            step=1,
                            style={"flex": 1}
                        ),
                        dmc.NumberInput(
                            id="live-buzzer",
                            label="Buzzer",
                            value=1,
                            min=0,
                            max=3,
                            step=1,
                            style={"flex": 1}
                        ),
                    ]),
                ])
            ], shadow="sm", padding="lg", radius="md", withBorder=True)
        ], span={"base": 12, "md": 6}),

        # Right column - Tabs with Preview and Queue
        dmc.GridCol([
            dmc.Card([
                dmc.Tabs([
                    dmc.TabsList([
                        dmc.TabsTab(
                            "Preview & Actions",
                            value="preview",
                            leftSection=DashIconify(icon="tabler:eye", width=16)
                        ),
                        dmc.TabsTab(
                            "Queue Monitor",
                            value="queue",
                            leftSection=DashIconify(icon="tabler:list", width=16)
                        ),
                    ]),

                    # Preview tab
                    dmc.TabsPanel(value="preview", children=[
                        dmc.Stack([
                            # Preview area
                            dmc.Stack([
                                dmc.Text("Message Preview", size="sm", fw=600, c="dimmed"),
                                dmc.ScrollArea(
                                    dmc.Code(
                                        id="message-preview",
                                        block=True,
                                        style={
                                            "whiteSpace": "pre",
                                            "fontFamily": "monospace",
                                            "fontSize": "12px",
                                            "lineHeight": "1.4",
                                            "minHeight": "200px"
                                        }
                                    ),
                                    h=200,
                                    style={
                                        "border": "1px solid var(--mantine-color-gray-3)",
                                        "borderRadius": "var(--mantine-radius-sm)"
                                    }
                                )
                            ]),

                            dmc.Divider(),

                            # Action buttons
                            dmc.Stack([
                                dmc.Button(
                                    "Send to Printer",
                                    id="live-print-btn",
                                    color="blue",
                                    leftSection=DashIconify(icon="tabler:printer-check", width=20),
                                    fullWidth=True,
                                    size="lg",
                                    disabled=not PRINTER_AVAILABLE
                                ),
                                dmc.Button(
                                    "Clear Message",
                                    id="clear-message-btn",
                                    variant="light",
                                    color="gray",
                                    leftSection=DashIconify(icon="tabler:eraser", width=20),
                                    fullWidth=True
                                ),
                            ]),

                            # Info alert
                            dmc.Alert(
                                [
                                    dmc.Stack([
                                        dmc.Text("Message the Creator", size="sm", fw=600),
                                        dmc.Text(
                                            "Your message will be sent to the creator's printer via the Star Micronics CloudPRNT API. Make it fun!",
                                            size="xs"
                                        )
                                    ])
                                ],
                                title="Direct to Creator",
                                icon=DashIconify(icon="tabler:info-circle", width=20),
                                color="blue" if PRINTER_AVAILABLE else "red",
                                variant="light"
                            )
                        ], gap="md")
                    ]),

                    # Queue tab
                    dmc.TabsPanel(value="queue", children=[
                        dmc.Stack([
                            dmc.Group([
                                dmc.Text("Current Queued Jobs", size="sm", fw=600, c="dimmed"),
                                dmc.Group([
                                    dmc.Button(
                                        "Refresh",
                                        id="queue-refresh-btn",
                                        size="xs",
                                        variant="light",
                                        leftSection=DashIconify(icon="tabler:refresh", width=14),
                                        disabled=not PRINTER_AVAILABLE
                                    ),
                                    dmc.Button(
                                        "Clear Queue",
                                        id="queue-clear-btn",
                                        size="xs",
                                        variant="light",
                                        color="red",
                                        leftSection=DashIconify(icon="tabler:trash", width=14),
                                        disabled=not PRINTER_AVAILABLE
                                    ),
                                ], gap="xs")
                            ], justify="space-between"),

                            # Queue grid
                            dag.AgGrid(
                                id="queue-grid",
                                columnDefs=[
                                    {"headerName": "Job ID", "field": "JobId", "flex": 1},
                                    {"headerName": "Name", "field": "JobName", "flex": 1},
                                    {"headerName": "Status", "field": "Status", "flex": 1},
                                    {"headerName": "Created", "field": "Created", "flex": 1},
                                ],
                                rowData=[],
                                defaultColDef={
                                    "sortable": True,
                                    "filter": True,
                                    "resizable": True,
                                },
                                dashGridOptions={
                                    "pagination": False,
                                    "domLayout": "autoHeight",
                                },
                                style={"height": "250px"},
                            ),

                            dmc.Text(
                                id="queue-status",
                                size="xs",
                                c="dimmed",
                                ta="center"
                            )
                        ], gap="sm")
                    ]),
                ], id="live-print-tabs", value="preview")
            ], shadow="sm", padding="lg", radius="md", withBorder=True)
        ], span={"base": 12, "md": 6})
    ], gutter="md"),

    # Star Document Markup reference
    dmc.Card([
        dmc.Stack([
            dmc.Title("Star Document Markup Quick Reference", order=4),
            dmc.SimpleGrid([
                dmc.Stack([
                    dmc.Text("Formatting", size="sm", fw=600, c="blue"),
                    dmc.Code("[bold]Bold text[normal]", block=True),
                    dmc.Code("[align: center]Centered", block=True),
                    dmc.Code("[align: left]Left aligned", block=True),
                    dmc.Code("[align: right]Right aligned", block=True),
                ], gap="xs"),
                dmc.Stack([
                    dmc.Text("Size", size="sm", fw=600, c="blue"),
                    dmc.Code("[mag: w 2; h 2]Big text", block=True),
                    dmc.Code("[mag: w 1; h 1]Normal", block=True),
                    dmc.Code("[invert]Inverted colors", block=True),
                    dmc.Code("[ul]Underlined text", block=True),
                ], gap="xs"),
                dmc.Stack([
                    dmc.Text("Special", size="sm", fw=600, c="blue"),
                    dmc.Code("[cut]Cut paper", block=True),
                    dmc.Code("[buzzer: pattern=A]Beep", block=True),
                    dmc.Code("---Dashed line---", block=True),
                    dmc.Code("===Double line===", block=True),
                ], gap="xs"),
            ], cols=3, spacing="md")
        ])
    ], shadow="sm", padding="lg", radius="md", withBorder=True, mt="lg")
], size="xl", px="md", py="xl")


@callback(
    Output('message-preview', 'children'),
    Input('message-content', 'value'),
)
def update_preview(content):
    """Update preview with formatted date/time"""
    if not content:
        return "No content to preview..."

    now = datetime.now()
    try:
        preview = content.format(
            date=now.strftime('%m/%d/%Y'),
            time=now.strftime('%I:%M %p')
        )
    except Exception:
        preview = content
    return preview


@callback(
    Output('message-content', 'value'),
    Input('message-template', 'value'),
    prevent_initial_call=True
)
def load_template(template):
    """Load default message template"""
    # Only one template now - custom message for creator
    return "[align: center]\n[bold][mag: w 2; h 2]Hello![normal][mag: w 1; h 1]\n\n[align: left]\nGreat documentation!\n\nI'm exploring dash-pos-printer and wanted to say hi.\n\n[align: center]\n{date} • {time}\n\n[cut]"


@callback(
    Output('notification-container', 'sendNotifications', allow_duplicate=True),
    Input('live-print-btn', 'n_clicks'),
    State('message-content', 'value'),
    State('live-copies', 'value'),
    State('live-buzzer', 'value'),
    prevent_initial_call=True
)
def handle_live_print(n_clicks, content, copies, buzzer):
    """Send message to actual printer"""
    # Explicitly check if button was clicked (prevent auto-fire on page load)
    if not n_clicks or n_clicks == 0:
        return no_update

    if not PRINTER_AVAILABLE:
        return [{
            "id": "printer-error",
            "title": "Printer Not Available",
            "message": "Please configure environment variables: STAR_MICRONICS, STAR_GROUP_PATH, STAR_DEVICE_ID",
            "action": "show",
            "color": "red",
            "icon": DashIconify(icon="tabler:alert-circle", width=20),
            "autoClose": 5000
        }]

    if not content:
        return [{
            "id": "no-content",
            "title": "No Content",
            "message": "Please enter a message to print",
            "action": "show",
            "color": "yellow",
            "icon": DashIconify(icon="tabler:alert-triangle", width=20),
            "autoClose": 3000
        }]

    try:
        # Get printer service (lazy initialization)
        service = get_printer_service()
        if not service:
            return [{
                "id": "service-init-error",
                "title": "Service Initialization Failed",
                "message": "Could not initialize printer service",
                "action": "show",
                "color": "red",
                "icon": DashIconify(icon="tabler:alert-circle", width=20),
                "autoClose": 5000
            }]

        # Format message with current date/time
        now = datetime.now()
        try:
            formatted_content = content.format(
                date=now.strftime('%m/%d/%Y'),
                time=now.strftime('%I:%M %p')
            )
        except Exception:
            formatted_content = content

        # Send to printer
        result = service.print_receipt(
            content=formatted_content,
            job_name=f"Visitor Message {now.strftime('%H:%M:%S')}",
            device_id=DEVICE_ID,
            copies=copies or 1,
            endbuzzer=buzzer or 0
        )

        # Check result
        if "error" in result:
            return [{
                "id": "print-error",
                "title": "Print Failed",
                "message": f"Error: {result['error']}",
                "action": "show",
                "color": "red",
                "icon": DashIconify(icon="tabler:alert-circle", width=20),
                "autoClose": 5000
            }]

        return [{
            "id": "print-success",
            "title": "Message Sent!",
            "message": f"Your message has been sent to the creator's printer!",
            "action": "show",
            "color": "green",
            "icon": DashIconify(icon="tabler:circle-check", width=20),
            "autoClose": 3000
        }]

    except Exception as e:
        return [{
            "id": "print-exception",
            "title": "Print Error",
            "message": f"Exception: {str(e)}",
            "action": "show",
            "color": "red",
            "icon": DashIconify(icon="tabler:bug", width=20),
            "autoClose": 5000
        }]


@callback(
    Output('message-content', 'value', allow_duplicate=True),
    Input('clear-message-btn', 'n_clicks'),
    prevent_initial_call=True
)
def clear_message(n_clicks):
    """Clear the message content"""
    return ""


@callback(
    [Output('queue-grid', 'rowData'),
     Output('queue-status', 'children')],
    [Input('queue-refresh-btn', 'n_clicks'),
     Input('queue-refresh-interval', 'n_intervals')],
    prevent_initial_call=True
)
def update_queue(n_clicks, n_intervals):
    """Fetch and update queue data"""
    if not PRINTER_AVAILABLE:
        return [], "Printer not available"

    try:
        # Get printer service (lazy initialization)
        service = get_printer_service()
        if not service:
            return [], "Service initialization failed"

        queue_data = service.get_queue(DEVICE_ID)

        if not queue_data or len(queue_data) == 0:
            return [], f"No jobs in queue - Last checked: {datetime.now().strftime('%I:%M:%S %p')}"

        # Format queue data for AG Grid
        rows = []
        for job in queue_data:
            rows.append({
                "JobId": job.get("JobId", "N/A"),
                "JobName": job.get("JobName", "Unnamed"),
                "Status": job.get("Status", "Pending"),
                "Created": job.get("Created", "Unknown"),
            })

        return rows, f"{len(rows)} job(s) in queue - Last updated: {datetime.now().strftime('%I:%M:%S %p')}"

    except Exception as e:
        return [], f"Error fetching queue: {str(e)}"


@callback(
    Output('notification-container', 'sendNotifications', allow_duplicate=True),
    Input('queue-clear-btn', 'n_clicks'),
    prevent_initial_call=True
)
def handle_clear_queue(n_clicks):
    """Clear all pending print jobs from queue"""
    # Explicitly check if button was clicked (prevent auto-fire on page load)
    if not n_clicks or n_clicks == 0:
        return no_update

    if not PRINTER_AVAILABLE:
        return [{
            "id": "queue-clear-error",
            "title": "Printer Not Available",
            "message": "Cannot clear queue - printer not configured",
            "action": "show",
            "color": "red",
            "icon": DashIconify(icon="tabler:alert-circle", width=20),
            "autoClose": 3000
        }]

    try:
        # Get printer service (lazy initialization)
        service = get_printer_service()
        if not service:
            return [{
                "id": "service-init-error",
                "title": "Service Initialization Failed",
                "message": "Could not initialize printer service",
                "action": "show",
                "color": "red",
                "icon": DashIconify(icon="tabler:alert-circle", width=20),
                "autoClose": 3000
            }]

        success = service.clear_queue(DEVICE_ID)

        if success:
            return [{
                "id": "queue-cleared",
                "title": "Queue Cleared",
                "message": "All pending print jobs have been cleared",
                "action": "show",
                "color": "green",
                "icon": DashIconify(icon="tabler:check", width=20),
                "autoClose": 2000
            }]
        else:
            return [{
                "id": "queue-clear-failed",
                "title": "Clear Failed",
                "message": "Could not clear the print queue",
                "action": "show",
                "color": "red",
                "icon": DashIconify(icon="tabler:alert-circle", width=20),
                "autoClose": 3000
            }]
    except Exception as e:
        return [{
            "id": "queue-clear-exception",
            "title": "Clear Error",
            "message": f"Error clearing queue: {str(e)}",
            "action": "show",
            "color": "red",
            "icon": DashIconify(icon="tabler:bug", width=20),
            "autoClose": 3000
        }]


@callback(
    [Output('queue-grid', 'rowData', allow_duplicate=True),
     Output('queue-status', 'children', allow_duplicate=True)],
    Input('live-print-tabs', 'value'),
    prevent_initial_call=True
)
def load_queue_on_tab_change(tab_value):
    """Load queue data when Queue Monitor tab is selected"""
    if tab_value != "queue":
        return no_update, no_update

    if not PRINTER_AVAILABLE:
        return [], "Printer not available"

    try:
        # Get printer service (lazy initialization)
        service = get_printer_service()
        if not service:
            return [], "Service initialization failed"

        queue_data = service.get_queue(DEVICE_ID)

        if not queue_data or len(queue_data) == 0:
            return [], f"No jobs in queue - Last checked: {datetime.now().strftime('%I:%M:%S %p')}"

        # Format queue data for AG Grid
        rows = []
        for job in queue_data:
            rows.append({
                "JobId": job.get("JobId", "N/A"),
                "JobName": job.get("JobName", "Unnamed"),
                "Status": job.get("Status", "Pending"),
                "Created": job.get("Created", "Unknown"),
            })

        return rows, f"{len(rows)} job(s) in queue - Last checked: {datetime.now().strftime('%I:%M:%S %p')}"

    except Exception as e:
        return [], f"Error fetching queue: {str(e)}"
```


This example connects to your real printer using the Star Micronics CloudPRNT API. Features:
- **Real API Connection** - Uses environment variables to connect to actual printer
- **Custom Message Composer** - Write any message with live preview
- **Template Library** - Pre-built templates for quick printing
- **Live Formatting** - Automatic date/time injection
- **Error Handling** - Comprehensive error messages and diagnostics
- **Connection Status** - Shows printer availability and configuration

**Environment Variables Required:**
- `STAR_MICRONICS` - Your Star Micronics API key
- `STAR_GROUP_PATH` - Your device group path
- `STAR_DEVICE_ID` - Target printer device ID
- `STAR_REGION` - API region (US or EU)

---

### Resources

#### Official Documentation
- [Star Micronics Docs](https://docs.star-m.com)
- [StarIO.Online Dashboard](https://api.stario.online) (US) / [EU](https://eu-api.stario.online)
- [CloudPRNT Specification](https://docs.star-m.com) - Available in dashboard Help section

#### Printer Manuals
- [mC-Print3 Manual](https://star-m.jp/products/s_print/mcprint31_oml/manual_en.html)
- [TSP100IV Manual](https://star-m.jp/products/s_print/tsp100iv_oml/manual_en.html)

#### Support
- [Plotly Community Forum](https://community.plotly.com)
- [GitHub Issues](https://github.com/pip-install-python/pip-documentation/issues)

---

*Last Updated: November 2025*
