# POS Printer > Cloud-based receipt printing for Dash applications using Star Micronics printers and CloudPRNT --- .. toc:: .. llms_copy::POS Printer # Star Micronics POS Printer Integration 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. --- .. exec::docs.dash_pos_printer.banner :code: false ## 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 ```python dash==3.3.0 dash-mantine-components==2.4.0 requests==2.32.4 python-dotenv==1.1.0 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 ``` --- ## Device Status Example Monitor printer health with real-time status cards: .. exec::docs.dash_pos_printer.device_status_example :code: false ```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: .. exec::docs.dash_pos_printer.print_receipt_example :code: false ```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 --- ## Live Printer Test Send custom messages directly to your actual Star Micronics printer: .. exec::docs.dash_pos_printer.live_print_example :code: false ```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 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: print(f"Warning: Printer service module not available: {e}") StarPrinterService = None # Get device ID from environment DEVICE_ID = os.environ.get('STAR_DEVICE_ID') GROUP_PATH = os.environ.get('STAR_GROUP_PATH', 'pipinstallpython') REGION = os.environ.get('STAR_REGION', 'US') PRINTER_AVAILABLE = PRINTER_SERVICE_AVAILABLE and DEVICE_ID is not None 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 dmc.Card([ dmc.Group([ dmc.Stack([ 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.Text(f"Device ID: {DEVICE_ID or 'Not set'}", size="sm", c="dimmed"), dmc.Text(f"Group Path: {GROUP_PATH or 'Not set'}", size="sm", c="dimmed"), dmc.Text(f"Region: {REGION or 'Not set'}", size="sm", c="dimmed"), ], gap="xs"), ]) ], 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) --- ## 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: .. exec::docs.dash_pos_printer.printer_dashboard_example :code: false ```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']}") ``` --- ## Configuration Example Step-by-step setup guide with visual feedback: .. exec::docs.dash_pos_printer.setup_guide_example :code: false ```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-vilot-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)}%" ``` --- ## 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 ``` --- ## 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) --- *Last Updated: November 2025* --- *Source: /components/dash-pos-printer* *Generated with dash-improve-my-llms*