22 KiB
22 KiB
EU-Utility API Reference
Complete API documentation for plugin developers
Version: 2.1.0
Last Updated: 2026-02-16
Table of Contents
- Getting Started
- PluginAPI - Core services access
- WidgetAPI - Overlay widget management
- ExternalAPI - REST endpoints and webhooks
- Event Bus - Pub/sub event system
- BasePlugin Class - Plugin base class reference
- Nexus API - Entropia Nexus integration
- Code Examples - Practical examples
- Best Practices
Getting Started
Importing the API
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:
if self.api.ocr_available():
text = self.api.recognize_text()
PluginAPI
The PluginAPI provides access to all core EU-Utility services.
Window Manager
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
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
# 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
# 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
# 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
# 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
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
# 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
# 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
# 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
from core.event_bus import (
SkillGainEvent,
LootEvent,
DamageEvent,
GlobalEvent,
ChatEvent,
EconomyEvent,
SystemEvent
)
Publishing Events
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
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
# 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
class MyPlugin(BasePlugin):
name = "My Plugin" # Display name
version = "1.0.0" # Version string
author = "Your Name" # Author name
description = "..." # Short description
Optional Attributes
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
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
# Get config value
theme = self.get_config('theme', default='dark')
# Set config value
self.set_config('theme', 'light')
Logging Methods
self.log_debug("Debug message")
self.log_info("Info message")
self.log_warning("Warning message")
self.log_error("Error message")
Utility Methods
# 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
# 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,armorsmobs,petsblueprints,materialslocations,teleporters,shops,vendors,planets,areasskillsenhancers,medicaltools,finders,excavators,refinersvehicles,decorations,furniturestoragecontainers,strongboxes
Getting Details
# 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
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
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
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
# ✅ 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
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
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
# ✅ 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
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
# ✅ Good
class LootTrackerPlugin(BasePlugin):
def on_loot_received(self):
pass
# ❌ Avoid
class lootTracker(BasePlugin):
def loot(self):
pass
7. Document Your Plugin
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:
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 - Detailed plugin tutorial
- Architecture Overview - System architecture
- Nexus API Reference - Entropia Nexus API
- Contributing - How to contribute