# EU-Utility API Reference > Complete API documentation for plugin developers **Version:** 2.1.0 **Last Updated:** 2026-02-16 --- ## Table of Contents 1. [Getting Started](#getting-started) 2. [PluginAPI](#pluginapi) - Core services access 3. [WidgetAPI](#widgetapi) - Overlay widget management 4. [ExternalAPI](#externalapi) - REST endpoints and webhooks 5. [Event Bus](#event-bus) - Pub/sub event system 6. [BasePlugin Class](#baseplugin-class) - Plugin base class reference 7. [Nexus API](#nexus-api) - Entropia Nexus integration 8. [Code Examples](#code-examples) - Practical examples 9. [Best Practices](#best-practices) --- ## Getting Started ### Importing the API ```python from core.api import get_api class MyPlugin(BasePlugin): def initialize(self): self.api = get_api() ``` ### API Availability Always check if a service is available before using it: ```python if self.api.ocr_available(): text = self.api.recognize_text() ``` --- ## PluginAPI The PluginAPI provides access to all core EU-Utility services. ### Window Manager ```python # Get EU window information window = api.get_eu_window() if window: print(f"Position: {window['x']}, {window['y']}") print(f"Size: {window['width']}x{window['height']}") print(f"Focused: {window['is_focused']}") # Check if EU is focused if api.is_eu_focused(): api.play_sound("alert.wav") # Bring EU to front api.bring_eu_to_front() # Check if EU window is visible is_visible = api.is_eu_visible() ``` ### OCR Service ```python # Check OCR availability if api.ocr_available(): # Read text from screen region text = api.recognize_text(region=(100, 100, 200, 50)) print(f"Found: {text}") # Alternative: Use BasePlugin helper method result = self.ocr_capture(region=(x, y, width, height)) # Returns: {'text': str, 'confidence': float, 'error': str or None} ``` ### Screenshot Service ```python # Check availability if api.screenshot_available(): # Capture full screen img = api.capture_screen() # Capture specific region img = api.capture_screen(region=(100, 100, 400, 300)) # Capture and save img = api.capture_screen( region=(100, 100, 400, 300), save_path="screenshot.png" ) # Alternative: Use BasePlugin helper img = self.capture_screen(full_screen=True) img = self.capture_region(x, y, width, height) ``` ### Log Reader ```python # Read recent log lines lines = api.read_log_lines(count=100) # Read logs since timestamp from datetime import datetime, timedelta recent = api.read_log_since(datetime.now() - timedelta(minutes=5)) # Alternative: Use BasePlugin helper lines = self.read_log(lines=50, filter_text="loot") ``` ### Data Store ```python # Store data (scoped to plugin) api.set_data("key", value) # Retrieve with default value = api.get_data("key", default=None) # Delete data api.delete_data("key") # Alternative: Use BasePlugin helpers self.save_data("key", value) data = self.load_data("key", default=None) self.delete_data("key") ``` ### HTTP Client ```python # GET request with caching result = api.http_get( "https://api.example.com/data", cache=True, cache_duration=3600 # 1 hour ) if result['success']: data = result['data'] else: error = result['error'] # POST request result = api.http_post( "https://api.example.com/submit", data={"key": "value"} ) # Alternative: Use BasePlugin helper response = self.http_get(url, cache_ttl=300, headers={}) ``` ### Audio ```python # Play sound file api.play_sound("assets/sounds/alert.wav", volume=0.7) # Simple beep api.beep() # Alternative: Use BasePlugin helpers self.play_sound("hof") # Predefined: 'hof', 'skill_gain', 'alert' self.set_volume(0.8) volume = self.get_volume() ``` ### Notifications ```python # Show toast notification api.show_notification( title="Loot Alert!", message="You found something valuable!", duration=5000, # milliseconds sound=True ) # Alternative: Use BasePlugin helpers self.notify(title, message, notification_type='info', sound=False) self.notify_info(title, message) self.notify_success(title, message) self.notify_warning(title, message) self.notify_error(title, message, sound=True) ``` ### Clipboard ```python # Copy to clipboard api.copy_to_clipboard("Text to copy") # Paste from clipboard text = api.paste_from_clipboard() # Alternative: Use BasePlugin helpers self.copy_to_clipboard(text) text = self.paste_from_clipboard() ``` ### Background Tasks ```python # Run function in background def heavy_computation(data): # Long running task import time time.sleep(2) return f"Processed: {data}" def on_complete(result): print(f"Done: {result}") def on_error(error): print(f"Error: {error}") task_id = api.run_task( heavy_computation, "my data", callback=on_complete, error_handler=on_error ) # Cancel task api.cancel_task(task_id) # Alternative: Use BasePlugin helper self.run_in_background(func, *args, priority='normal', on_complete=cb, on_error=err_cb) ``` --- ## WidgetAPI Manage overlay widgets - floating UI components. ### Getting Started ```python from core.api import get_widget_api widget_api = get_widget_api() # Create widget widget = widget_api.create_widget( name="loot_tracker", title="Loot Tracker", size=(400, 300), position=(100, 100) ) widget.show() ``` ### Widget Operations ```python # Show/hide widget.show() widget.hide() # Position widget.move(500, 200) x, y = widget.position # Size widget.resize(400, 300) width, height = widget.size # Opacity (0.0 - 1.0) widget.set_opacity(0.8) # Lock/unlock (prevent dragging) widget.set_locked(True) # Minimize/restore widget.minimize() widget.restore() # Close widget.close() ``` ### Widget Management ```python # Get existing widget widget = widget_api.get_widget("loot_tracker") # Show/hide specific widget widget_api.show_widget("loot_tracker") widget_api.hide_widget("loot_tracker") # Close widget widget_api.close_widget("loot_tracker") # Global operations widget_api.show_all_widgets() widget_api.hide_all_widgets() widget_api.close_all_widgets() widget_api.set_all_opacity(0.8) widget_api.lock_all() widget_api.unlock_all() ``` ### Layout Helpers ```python # Arrange widgets widget_api.arrange_widgets(layout="grid", spacing=10) widget_api.arrange_widgets(layout="horizontal") widget_api.arrange_widgets(layout="vertical") widget_api.arrange_widgets(layout="cascade") # Snap to grid widget_api.snap_to_grid(grid_size=10) ``` ### Widget Events ```python # Handle events widget.on('moved', lambda data: print(f"Moved to {data['x']}, {data['y']}")) widget.on('resized', lambda data: print(f"Sized to {data['width']}x{data['height']}")) widget.on('closing', lambda: print("Widget closing")) widget.on('closed', lambda: print("Widget closed")) widget.on('update', lambda data: print(f"Update: {data}")) ``` --- ## ExternalAPI REST endpoints, webhooks, and third-party integrations. ### Getting Started ```python from core.api import get_external_api ext = get_external_api() # Start server ext.start_server(port=8080) # Check status print(ext.get_status()) ``` ### REST Endpoints ```python # Using decorator @ext.endpoint("stats", methods=["GET"]) def get_stats(): return {"kills": 100, "loot": "50 PED"} @ext.endpoint("loot", methods=["POST"]) def record_loot(data): save_loot(data) return {"status": "saved"} # Programmatic registration def get_stats_handler(params): return {"kills": 100} ext.register_endpoint("stats", get_stats_handler, methods=["GET"]) # Unregister ext.unregister_endpoint("stats") ``` ### Incoming Webhooks ```python # Register webhook handler def handle_discord(payload): print(f"Discord: {payload}") return {"status": "ok"} ext.register_webhook( name="discord", handler=handle_discord, secret="my_secret" # Optional HMAC verification ) # POST to: http://localhost:8080/webhook/discord ``` ### Outgoing Webhooks ```python # POST to external webhook result = ext.post_webhook( "https://discord.com/api/webhooks/...", {"content": "Hello from EU-Utility!"} ) if result['success']: print("Sent!") else: print(f"Error: {result['error']}") ``` --- ## Event Bus Typed publish-subscribe event system for inter-plugin communication. ### Event Types ```python from core.event_bus import ( SkillGainEvent, LootEvent, DamageEvent, GlobalEvent, ChatEvent, EconomyEvent, SystemEvent ) ``` ### Publishing Events ```python from core.event_bus import LootEvent, SkillGainEvent # Publish loot event self.publish_typed(LootEvent( mob_name="Daikiba", items=[{"name": "Animal Oil", "value": 0.05}], total_tt_value=0.05 )) # Publish skill gain self.publish_typed(SkillGainEvent( skill_name="Rifle", skill_value=25.5, gain_amount=0.01 )) # Legacy event publishing api.publish("my_plugin.event", {"data": "value"}) ``` ### Subscribing to Events ```python from core.event_bus import LootEvent, SkillGainEvent class MyPlugin(BasePlugin): def initialize(self): # Subscribe to loot events self.sub_id = self.subscribe_typed( LootEvent, self.on_loot ) # Subscribe with filtering self.sub_id2 = self.subscribe_typed( LootEvent, self.on_dragon_loot, mob_types=["Dragon", "Drake"] ) # Subscribe to specific skills self.sub_id3 = self.subscribe_typed( SkillGainEvent, self.on_combat_skill, skill_names=["Rifle", "Pistol", "Melee"] ) def on_loot(self, event: LootEvent): print(f"Loot from {event.mob_name}: {event.items}") def on_dragon_loot(self, event: LootEvent): print(f"Dragon loot! Total TT: {event.total_tt_value}") def shutdown(self): # Clean up subscriptions self.unsubscribe_typed(self.sub_id) self.unsubscribe_typed(self.sub_id2) self.unsubscribe_typed(self.sub_id3) # Or: self.unsubscribe_all_typed() ``` ### Event Attributes | Event | Attributes | |-------|------------| | `SkillGainEvent` | `skill_name`, `skill_value`, `gain_amount` | | `LootEvent` | `mob_name`, `items`, `total_tt_value`, `position` | | `DamageEvent` | `damage_amount`, `damage_type`, `is_critical`, `target_name` | | `GlobalEvent` | `player_name`, `achievement_type`, `value`, `item_name` | | `ChatEvent` | `channel`, `sender`, `message` | | `EconomyEvent` | `transaction_type`, `amount`, `currency`, `description` | | `SystemEvent` | `message`, `severity` | ### Legacy Event Bus ```python # Subscribe sub_id = api.subscribe("loot", on_loot_callback) # Unsubscribe api.unsubscribe(sub_id) # Publish api.publish("event_type", {"key": "value"}) # Get history history = api.get_event_history("loot", limit=10) ``` --- ## BasePlugin Class Complete reference for the base plugin class. ### Required Attributes ```python class MyPlugin(BasePlugin): name = "My Plugin" # Display name version = "1.0.0" # Version string author = "Your Name" # Author name description = "..." # Short description ``` ### Optional Attributes ```python class MyPlugin(BasePlugin): icon = "path/to/icon.png" # Icon path hotkey = "ctrl+shift+y" # Legacy single hotkey hotkeys = [ # Multi-hotkey format { 'action': 'toggle', 'description': 'Toggle My Plugin', 'default': 'ctrl+shift+m', 'config_key': 'myplugin_toggle' } ] enabled = True # Start enabled dependencies = { # Dependencies 'pip': ['requests', 'numpy'], 'plugins': ['other_plugin'], 'optional': {'pillow': 'Image processing'} } ``` ### Lifecycle Methods ```python def initialize(self) -> None: """Called when plugin is loaded.""" self.api = get_api() self.log_info("Initialized!") def get_ui(self) -> QWidget: """Return the plugin's UI widget.""" widget = QWidget() # ... setup UI ... return widget def on_show(self) -> None: """Called when overlay becomes visible.""" pass def on_hide(self) -> None: """Called when overlay is hidden.""" pass def on_hotkey(self) -> None: """Called when hotkey is pressed.""" pass def shutdown(self) -> None: """Called when app is closing. Cleanup resources.""" self.unsubscribe_all_typed() super().shutdown() ``` ### Config Methods ```python # Get config value theme = self.get_config('theme', default='dark') # Set config value self.set_config('theme', 'light') ``` ### Logging Methods ```python self.log_debug("Debug message") self.log_info("Info message") self.log_warning("Warning message") self.log_error("Error message") ``` ### Utility Methods ```python # Format currency ped_text = self.format_ped(123.45) # "123.45 PED" pec_text = self.format_pec(50) # "50 PEC" # Calculate DPP dpp = self.calculate_dpp(damage=45.0, ammo=100, decay=2.5) # Calculate markup markup = self.calculate_markup(price=150.0, tt=100.0) # 150.0% ``` --- ## Nexus API Entropia Nexus integration for game data. ### Searching ```python # Search for items results = self.nexus_search("ArMatrix", entity_type="items") # Search for mobs mobs = self.nexus_search("Atrox", entity_type="mobs") # Search for locations locations = self.nexus_search("Fort", entity_type="locations") ``` ### Entity Types - `items`, `weapons`, `armors` - `mobs`, `pets` - `blueprints`, `materials` - `locations`, `teleporters`, `shops`, `vendors`, `planets`, `areas` - `skills` - `enhancers`, `medicaltools`, `finders`, `excavators`, `refiners` - `vehicles`, `decorations`, `furniture` - `storagecontainers`, `strongboxes` ### Getting Details ```python # Get item details details = self.nexus_get_item_details("armatrix_lp-35") if details: print(f"Name: {details['name']}") print(f"TT Value: {details['tt_value']} PED") # Get market data market = self.nexus_get_market_data("armatrix_lp-35") if market: print(f"Current markup: {market['current_markup']:.1f}%") ``` --- ## Code Examples ### Example 1: Simple Calculator Plugin ```python from plugins.base_plugin import BasePlugin from PyQt6.QtWidgets import ( QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QPushButton ) class MarkupCalculatorPlugin(BasePlugin): """Calculate markup percentages.""" name = "Markup Calculator" version = "1.0.0" author = "Tutorial" description = "Calculate item markup" hotkey = "ctrl+shift+m" def initialize(self): self.log_info("Markup Calculator initialized!") def get_ui(self): widget = QWidget() layout = QVBoxLayout(widget) # Title title = QLabel("Markup Calculator") title.setStyleSheet("color: #4a9eff; font-size: 18px; font-weight: bold;") layout.addWidget(title) # TT Value input layout.addWidget(QLabel("TT Value:")) self.tt_input = QLineEdit() self.tt_input.setPlaceholderText("100.00") layout.addWidget(self.tt_input) # Market Price input layout.addWidget(QLabel("Market Price:")) self.price_input = QLineEdit() self.price_input.setPlaceholderText("150.00") layout.addWidget(self.price_input) # Calculate button calc_btn = QPushButton("Calculate") calc_btn.clicked.connect(self.calculate) layout.addWidget(calc_btn) # Result self.result_label = QLabel("Markup: -") self.result_label.setStyleSheet("color: #ffc107; font-size: 16px;") layout.addWidget(self.result_label) layout.addStretch() return widget def calculate(self): try: tt = float(self.tt_input.text() or 0) price = float(self.price_input.text() or 0) markup = self.calculate_markup(price, tt) self.result_label.setText(f"Markup: {markup:.1f}%") except ValueError: self.result_label.setText("Invalid input") ``` ### Example 2: Event-Driven Tracker ```python from plugins.base_plugin import BasePlugin from PyQt6.QtWidgets import QWidget, QVBoxLayout, QLabel, QListWidget from core.event_bus import LootEvent, SkillGainEvent class ActivityTrackerPlugin(BasePlugin): """Track recent activity from events.""" name = "Activity Tracker" version = "1.0.0" def initialize(self): self.subscriptions = [] # Subscribe to multiple event types sub1 = self.subscribe_typed(LootEvent, self.on_loot) sub2 = self.subscribe_typed(SkillGainEvent, self.on_skill) self.subscriptions.extend([sub1, sub2]) self.log_info("Activity Tracker initialized") def get_ui(self): widget = QWidget() layout = QVBoxLayout(widget) layout.addWidget(QLabel("Recent Activity:")) self.activity_list = QListWidget() layout.addWidget(self.activity_list) return widget def on_loot(self, event: LootEvent): text = f"Loot: {event.mob_name} - {event.total_tt_value:.2f} PED" self.activity_list.insertItem(0, text) def on_skill(self, event: SkillGainEvent): text = f"Skill: {event.skill_name} +{event.gain_amount:.4f}" self.activity_list.insertItem(0, text) def shutdown(self): for sub_id in self.subscriptions: self.unsubscribe_typed(sub_id) ``` ### Example 3: Background Task Plugin ```python from plugins.base_plugin import BasePlugin from PyQt6.QtWidgets import ( QWidget, QVBoxLayout, QPushButton, QLabel, QProgressBar ) class DataFetcherPlugin(BasePlugin): """Fetch data in background.""" name = "Data Fetcher" version = "1.0.0" def initialize(self): self.log_info("Data Fetcher initialized") def get_ui(self): widget = QWidget() layout = QVBoxLayout(widget) self.status_label = QLabel("Ready") layout.addWidget(self.status_label) self.progress = QProgressBar() self.progress.setRange(0, 0) # Indeterminate self.progress.hide() layout.addWidget(self.progress) fetch_btn = QPushButton("Fetch Data") fetch_btn.clicked.connect(self.fetch_data) layout.addWidget(fetch_btn) layout.addStretch() return widget def fetch_data(self): self.status_label.setText("Fetching...") self.progress.show() # Run in background self.run_in_background( self._fetch_from_api, priority='normal', on_complete=self._on_fetch_complete, on_error=self._on_fetch_error ) def _fetch_from_api(self): # This runs in background thread import time time.sleep(2) # Simulate API call return {"items": ["A", "B", "C"], "count": 3} def _on_fetch_complete(self, result): # This runs in main thread - safe to update UI self.progress.hide() self.status_label.setText(f"Got {result['count']} items") self.notify_success("Data Fetched", f"Retrieved {result['count']} items") def _on_fetch_error(self, error): self.progress.hide() self.status_label.setText(f"Error: {error}") self.notify_error("Fetch Failed", str(error)) ``` --- ## Best Practices ### 1. Always Use BasePlugin Helpers ```python # ✅ Good - Uses helper methods self.save_data("key", value) data = self.load_data("key", default) # ❌ Avoid - Direct API access when helper exists self.api.set_data("key", value) ``` ### 2. Handle Errors Gracefully ```python def initialize(self): try: self.api = get_api() self.log_info("API connected") except Exception as e: self.log_error(f"Failed to get API: {e}") # Continue with limited functionality ``` ### 3. Clean Up Resources ```python def shutdown(self): # Unsubscribe from events self.unsubscribe_all_typed() # Save any pending data self.save_data("pending", self.pending_data) super().shutdown() ``` ### 4. Don't Block the Main Thread ```python # ✅ Good - Use background tasks def heavy_operation(self): self.run_in_background( self._do_heavy_work, on_complete=self._update_ui ) # ❌ Avoid - Blocks UI def heavy_operation(self): result = self._do_heavy_work() # Blocks! self._update_ui(result) ``` ### 5. Use Type Hints ```python from typing import Optional, Dict, Any, List from core.event_bus import LootEvent def on_loot(self, event: LootEvent) -> None: items: List[Dict[str, Any]] = event.items total: float = event.total_tt_value ``` ### 6. Follow Naming Conventions ```python # ✅ Good class LootTrackerPlugin(BasePlugin): def on_loot_received(self): pass # ❌ Avoid class lootTracker(BasePlugin): def loot(self): pass ``` ### 7. Document Your Plugin ```python class MyPlugin(BasePlugin): """ Brief description of what this plugin does. Features: - Feature 1 - Feature 2 Usage: 1. Step 1 2. Step 2 Hotkeys: - Ctrl+Shift+Y: Toggle plugin """ ``` --- ## Error Handling All APIs provide specific exceptions: ```python from core.api import ( PluginAPIError, ServiceNotAvailableError, ExternalAPIError ) try: text = api.recognize_text((0, 0, 100, 100)) except ServiceNotAvailableError: print("OCR not available") except PluginAPIError as e: print(f"API error: {e}") ``` --- ## See Also - [Plugin Development Guide](./docs/PLUGIN_DEVELOPMENT.md) - Detailed plugin tutorial - [Architecture Overview](./ARCHITECTURE.md) - System architecture - [Nexus API Reference](./docs/NEXUS_API_REFERENCE.md) - Entropia Nexus API - [Contributing](./CONTRIBUTING.md) - How to contribute