Application Management
The Cloud API provides methods for creating, retrieving, and updating Doover applications. Applications define deployable packages that can run on devices or as cloud tasks.
Application Class
The Application class represents a Doover application configuration.
Attributes
| Attribute | Type | Description |
|---|---|---|
| key | str | Unique identifier for the application |
| name | str | Application name (required) |
| display_name | str | Human-readable display name (required) |
| type | Type | Application type (device, processor, task, integration) |
| visibility | Visibility | Visibility level (core, public, private, internal) |
| allow_many | bool | Whether multiple instances can be deployed |
| description | str | Short description |
| long_description | str | Detailed description (can reference a file path) |
| depends_on | list[Object] | List of dependency application keys |
| owner_org | Object | Organization that owns the application |
| code_repo | Object | Code repository reference |
| repo_branch | str | Repository branch (default: "main") |
| image_name | str | Docker image name |
| build_args | str | Docker build arguments |
| container_registry_profile | Object | Container registry configuration |
| config_schema | dict | Configuration schema for the application |
| staging_config | dict | Staging environment configuration overrides |
| base_path | Path | Local filesystem path to application |
Properties
| Property | Type | Description |
|---|---|---|
| src_directory | Path | Path to the source code directory |
Application Types
from pydoover.cloud.api.application import Type Type.device # "DEV" - Device application Type.processor # "PRO" - Processor application Type.task # "TAS" - Task application Type.integration # "INT" - Integration application
Visibility Levels
from pydoover.cloud.api.application import Visibility Visibility.core # "COR" - Core system application Visibility.public # "PUB" - Publicly visible Visibility.private # "PRI" - Private to organization Visibility.internal # "INT" - Internal use only
Retrieving Applications
List All Applications
from pydoover.cloud.api import Client
client = Client()
# Get all available applications
apps = client.get_applications()
for app in apps:
print(f"{app['name']}: {app['display_name']}")
Get Specific Application
app = client.get_application("app-key-here")
print(f"Application: {app.display_name}")
print(f"Type: {app.type}")
print(f"Description: {app.description}")
Creating Applications
From Configuration File
Applications are typically defined in a doover_config.json file:
{
"my-app": {
"name": "my-app",
"display_name": "My Application",
"type": "DEV",
"visibility": "PRI",
"description": "A sample application",
"long_description": "README.md",
"owner_org_key": "org-uuid",
"code_repo_key": "repo-uuid",
"repo_branch": "main",
"config_schema": {
"type": "object",
"properties": {
"interval": {
"type": "integer",
"default": 60
}
}
}
}
}
Load and create the application:
from pathlib import Path
from pydoover.cloud.api import Client
from pydoover.cloud.api.application import Application
import json
client = Client()
# Load configuration
app_dir = Path("/path/to/app")
config_path = app_dir / "doover_config.json"
with open(config_path) as f:
config = json.load(f)
# Create Application instance
app = Application.from_config(config["my-app"], app_base=app_dir)
# Create on the server
app_key = client.create_application(app)
print(f"Created application with key: {app_key}")
Programmatic Creation
from pathlib import Path
from pydoover.cloud.api import Client
from pydoover.cloud.api.application import Application, Type, Visibility
from pydoover.cloud.api.object import Object
client = Client()
app = Application(
key=None, # Will be assigned on creation
name="sensor-monitor",
display_name="Sensor Monitor",
app_type=Type.device,
visibility=Visibility.private,
allow_many=True,
description="Monitors sensor data",
long_description="Detailed description of the sensor monitoring application.",
depends_on=[],
owner_org=Object(key="your-org-uuid"),
code_repo=Object(key="your-repo-uuid"),
repo_branch="main",
image_name="sensor-monitor",
build_args="",
container_registry_profile=Object(key=None),
config_schema={
"type": "object",
"properties": {
"poll_interval": {"type": "integer", "default": 30}
}
},
staging_config={},
app_base=Path("/path/to/app")
)
app_key = client.create_application(app)
print(f"Created: {app_key}")
Updating Applications
# Get existing application
app = client.get_application("app-key")
# Modify properties
app.description = "Updated description"
app.config_schema["properties"]["new_setting"] = {
"type": "string",
"default": "value"
}
# Update on server
client.update_application(app)
Staging Environment
Use the is_staging flag to deploy to staging:
# Create in staging environment app_key = client.create_application(app, is_staging=True) # Update in staging environment client.update_application(app, is_staging=True)
The staging_config dictionary can override main configuration values for staging deployments.
Deployment Data
Applications can include deployment data (Docker compose files, configurations):
# Application structure:
# /path/to/app/
# doover_config.json
# deployment/
# docker-compose.yml
# config/
# settings.yaml
app = Application.from_config(config, app_base=Path("/path/to/app"))
# Create with deployment data included
app_key = client.create_application(app) # Automatically includes deployment/ folder
The deployment/ directory is zipped and uploaded as base64-encoded data.
Saving Configuration
Save application configuration back to disk:
app = client.get_application("app-key")
# Set base path if not already set
app.base_path = Path("/path/to/app")
# Save to doover_config.json
app.save_to_disk()
Configuration Schema
The config_schema defines what configuration options are available when deploying the application:
config_schema = {
"type": "object",
"properties": {
"poll_interval": {
"type": "integer",
"default": 60,
"description": "Data polling interval in seconds"
},
"api_key": {
"type": "string",
"description": "API key for external service"
},
"enabled_features": {
"type": "array",
"items": {"type": "string"},
"default": ["logging", "metrics"]
}
},
"required": ["api_key"]
}
Complete Example
from pathlib import Path
import json
from pydoover.cloud.api import Client
from pydoover.cloud.api.application import Application, Type, Visibility
from pydoover.cloud.api.object import Object
client = Client()
# Define application
app = Application(
key=None,
name="temperature-logger",
display_name="Temperature Logger",
app_type=Type.device,
visibility=Visibility.private,
allow_many=True,
description="Logs temperature readings from connected sensors",
long_description="""
# Temperature Logger
This application reads temperature data from connected sensors and logs
the readings to the Doover platform.
## Features
- Multi-sensor support
- Configurable polling intervals
- Automatic unit conversion
- Alert thresholds
""",
depends_on=[],
owner_org=Object(key="org-uuid"),
code_repo=Object(key="repo-uuid"),
repo_branch="main",
image_name="temperature-logger",
build_args="",
container_registry_profile=Object(key=None),
config_schema={
"type": "object",
"properties": {
"poll_interval": {
"type": "integer",
"default": 30,
"minimum": 5,
"description": "Sensor polling interval in seconds"
},
"temperature_unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"default": "celsius"
},
"alert_threshold": {
"type": "number",
"default": 50.0,
"description": "Temperature threshold for alerts"
}
}
},
staging_config={
"poll_interval": 5 # Faster polling in staging
},
app_base=Path("/home/user/apps/temperature-logger")
)
# Create application
try:
app_key = client.create_application(app)
print(f"Created application: {app_key}")
except Exception as e:
print(f"Failed to create application: {e}")
# Later, update the application
app = client.get_application(app_key)
app.description = "Updated: Logs temperature readings with enhanced alerting"
app.config_schema["properties"]["alert_email"] = {
"type": "string",
"format": "email",
"description": "Email address for alerts"
}
client.update_application(app)
print("Application updated successfully")
# List all applications
print("\nAll applications:")
for app_data in client.get_applications():
print(f" - {app_data['name']}: {app_data.get('description', 'No description')}")
Tunnel Management
The Cloud API also provides methods for managing remote access tunnels to devices.
Listing Tunnels
# Get all tunnels for an agent
tunnels = client.get_tunnels(agent_id="agent-uuid")
for tunnel in tunnels:
print(f"Tunnel: {tunnel['id']} - {tunnel.get('status')}")
# Get with choices (for UI selection)
tunnels = client.get_tunnels(agent_id="agent-uuid", show_choices=True)
Creating Tunnels
tunnel = client.create_tunnel(
agent_id="agent-uuid",
target_address="192.168.1.100",
target_port=8080,
protocol="http"
)
print(f"Created tunnel: {tunnel['id']}")
Managing Tunnel State
tunnel_id = "tunnel-uuid" # Activate tunnel client.activate_tunnel(tunnel_id) # Deactivate tunnel client.deactivate_tunnel(tunnel_id) # Update tunnel configuration client.patch_tunnel(tunnel_id, target_port=8443) # Delete tunnel client.delete_tunnel(tunnel_id)
Legacy Tunnel Endpoints
# Create ngrok-style endpoints
urls = client.create_tunnel_endpoints(
agent_id="agent-uuid",
endpoint_type="http",
amount=2
)
print(f"Created endpoints: {urls}")
# Get existing endpoints
endpoints = client.get_tunnel_endpoints(
agent_id="agent-uuid",
endpoint_type="http"
)
Error Handling
from pydoover.cloud.api import Client, NotFound, HTTPException
client = Client()
try:
app = client.get_application("nonexistent-key")
except NotFound:
print("Application not found")
except HTTPException as e:
print(f"API error: {e}")
try:
client.create_application(app)
except HTTPException as e:
if "already exists" in str(e):
print("Application already exists, updating instead")
client.update_application(app)
else:
raise