🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
313 lines
9.9 KiB
Python
313 lines
9.9 KiB
Python
"""Update Service Layer.
|
|
|
|
This service provides software update operations that can be consumed by
|
|
multiple interfaces (REST API, BLE GATT, etc.).
|
|
"""
|
|
from typing import Optional, Dict, Any
|
|
|
|
from ..managers.update_manager import UpdateManager, UpdateProgress, UpdateStatus
|
|
from .pm3_service import PM3ServiceError, PM3ServiceResult
|
|
|
|
|
|
class UpdateService:
|
|
"""Update service layer for software update management.
|
|
|
|
This service can be used by multiple interfaces:
|
|
- REST API (FastAPI endpoints)
|
|
- BLE GATT (Bluetooth handlers)
|
|
- Plugin system (future)
|
|
|
|
It encapsulates:
|
|
- Update checking
|
|
- Update downloading
|
|
- Update installation
|
|
- Progress monitoring
|
|
"""
|
|
|
|
def __init__(self, update_manager: Optional[UpdateManager] = None):
|
|
"""Initialize update service.
|
|
|
|
Args:
|
|
update_manager: Update manager instance (creates new if not provided)
|
|
"""
|
|
from ..managers.update_manager import get_update_manager
|
|
self.update_manager = update_manager or get_update_manager()
|
|
|
|
async def check_for_updates(self) -> PM3ServiceResult:
|
|
"""Check for available updates.
|
|
|
|
Returns:
|
|
PM3ServiceResult with update availability and information
|
|
"""
|
|
try:
|
|
result = await self.update_manager.check_for_updates()
|
|
|
|
return PM3ServiceResult(
|
|
success=True,
|
|
data=result
|
|
)
|
|
|
|
except Exception as e:
|
|
return PM3ServiceResult(
|
|
success=False,
|
|
error=PM3ServiceError(
|
|
code="update_check_error",
|
|
message="Failed to check for updates",
|
|
details=str(e)
|
|
)
|
|
)
|
|
|
|
async def download_update(self) -> PM3ServiceResult:
|
|
"""Download the latest available update.
|
|
|
|
Returns:
|
|
PM3ServiceResult indicating download success/failure
|
|
"""
|
|
try:
|
|
success = await self.update_manager.download_update()
|
|
|
|
if success:
|
|
return PM3ServiceResult(
|
|
success=True,
|
|
data={
|
|
"message": "Update downloaded successfully",
|
|
"ready_to_install": True
|
|
}
|
|
)
|
|
else:
|
|
return PM3ServiceResult(
|
|
success=False,
|
|
error=PM3ServiceError(
|
|
code="download_failed",
|
|
message="Update download failed"
|
|
)
|
|
)
|
|
|
|
except ValueError as e:
|
|
# No update available
|
|
return PM3ServiceResult(
|
|
success=False,
|
|
error=PM3ServiceError(
|
|
code="no_update_available",
|
|
message=str(e)
|
|
)
|
|
)
|
|
except Exception as e:
|
|
return PM3ServiceResult(
|
|
success=False,
|
|
error=PM3ServiceError(
|
|
code="update_download_error",
|
|
message="Error downloading update",
|
|
details=str(e)
|
|
)
|
|
)
|
|
|
|
async def install_update(self) -> PM3ServiceResult:
|
|
"""Install the downloaded update.
|
|
|
|
Returns:
|
|
PM3ServiceResult indicating installation success/failure
|
|
"""
|
|
try:
|
|
success = await self.update_manager.install_update()
|
|
|
|
if success:
|
|
return PM3ServiceResult(
|
|
success=True,
|
|
data={
|
|
"message": "Update installed successfully",
|
|
"requires_restart": True
|
|
}
|
|
)
|
|
else:
|
|
return PM3ServiceResult(
|
|
success=False,
|
|
error=PM3ServiceError(
|
|
code="installation_failed",
|
|
message="Update installation failed"
|
|
)
|
|
)
|
|
|
|
except ValueError as e:
|
|
# No update downloaded
|
|
return PM3ServiceResult(
|
|
success=False,
|
|
error=PM3ServiceError(
|
|
code="no_update_downloaded",
|
|
message=str(e)
|
|
)
|
|
)
|
|
except Exception as e:
|
|
return PM3ServiceResult(
|
|
success=False,
|
|
error=PM3ServiceError(
|
|
code="update_install_error",
|
|
message="Error installing update",
|
|
details=str(e)
|
|
)
|
|
)
|
|
|
|
async def get_progress(self) -> PM3ServiceResult:
|
|
"""Get current update progress.
|
|
|
|
Returns:
|
|
PM3ServiceResult with progress information
|
|
"""
|
|
try:
|
|
progress = await self.update_manager.get_progress()
|
|
|
|
return PM3ServiceResult(
|
|
success=True,
|
|
data={
|
|
"status": progress.status.value,
|
|
"current_version": progress.current_version,
|
|
"available_version": progress.available_version,
|
|
"download_progress": progress.download_progress,
|
|
"error_message": progress.error_message,
|
|
"last_check": progress.last_check
|
|
}
|
|
)
|
|
|
|
except Exception as e:
|
|
return PM3ServiceResult(
|
|
success=False,
|
|
error=PM3ServiceError(
|
|
code="progress_error",
|
|
message="Failed to get update progress",
|
|
details=str(e)
|
|
)
|
|
)
|
|
|
|
async def get_release_notes(
|
|
self,
|
|
version: Optional[str] = None
|
|
) -> PM3ServiceResult:
|
|
"""Get release notes for a specific version or latest.
|
|
|
|
Args:
|
|
version: Version to get notes for (latest if None)
|
|
|
|
Returns:
|
|
PM3ServiceResult with release notes
|
|
"""
|
|
try:
|
|
notes = await self.update_manager.get_release_notes(version)
|
|
|
|
return PM3ServiceResult(
|
|
success=True,
|
|
data={
|
|
"version": version or "latest",
|
|
"release_notes": notes
|
|
}
|
|
)
|
|
|
|
except Exception as e:
|
|
return PM3ServiceResult(
|
|
success=False,
|
|
error=PM3ServiceError(
|
|
code="release_notes_error",
|
|
message="Failed to get release notes",
|
|
details=str(e)
|
|
)
|
|
)
|
|
|
|
async def check_and_download_update(self) -> PM3ServiceResult:
|
|
"""Combined operation: check for updates and download if available.
|
|
|
|
Returns:
|
|
PM3ServiceResult with check and download status
|
|
"""
|
|
try:
|
|
# First check for updates
|
|
check_result = await self.check_for_updates()
|
|
|
|
if not check_result.success:
|
|
return check_result
|
|
|
|
# If update available, download it
|
|
if check_result.data.get("update_available"):
|
|
download_result = await self.download_update()
|
|
|
|
if download_result.success:
|
|
return PM3ServiceResult(
|
|
success=True,
|
|
data={
|
|
"message": "Update checked and downloaded",
|
|
"update_info": check_result.data,
|
|
"ready_to_install": True
|
|
}
|
|
)
|
|
else:
|
|
return download_result
|
|
else:
|
|
return PM3ServiceResult(
|
|
success=True,
|
|
data={
|
|
"message": "System is up to date",
|
|
"update_available": False,
|
|
"current_version": check_result.data.get("current_version")
|
|
}
|
|
)
|
|
|
|
except Exception as e:
|
|
return PM3ServiceResult(
|
|
success=False,
|
|
error=PM3ServiceError(
|
|
code="check_download_error",
|
|
message="Error checking and downloading update",
|
|
details=str(e)
|
|
)
|
|
)
|
|
|
|
async def full_update(self) -> PM3ServiceResult:
|
|
"""Full update workflow: check, download, and install.
|
|
|
|
Returns:
|
|
PM3ServiceResult with complete update status
|
|
"""
|
|
try:
|
|
# Check for updates
|
|
check_result = await self.check_for_updates()
|
|
if not check_result.success:
|
|
return check_result
|
|
|
|
if not check_result.data.get("update_available"):
|
|
return PM3ServiceResult(
|
|
success=True,
|
|
data={
|
|
"message": "System is already up to date",
|
|
"update_performed": False
|
|
}
|
|
)
|
|
|
|
# Download update
|
|
download_result = await self.download_update()
|
|
if not download_result.success:
|
|
return download_result
|
|
|
|
# Install update
|
|
install_result = await self.install_update()
|
|
if not install_result.success:
|
|
return install_result
|
|
|
|
return PM3ServiceResult(
|
|
success=True,
|
|
data={
|
|
"message": "Update completed successfully",
|
|
"update_performed": True,
|
|
"previous_version": check_result.data.get("current_version"),
|
|
"new_version": check_result.data.get("latest_version"),
|
|
"requires_restart": True
|
|
}
|
|
)
|
|
|
|
except Exception as e:
|
|
return PM3ServiceResult(
|
|
success=False,
|
|
error=PM3ServiceError(
|
|
code="full_update_error",
|
|
message="Error performing full update",
|
|
details=str(e)
|
|
)
|
|
)
|