use crate::constants; use crate::devices::device::DeviceId; use crate::energy::energy_actor; use crate::ota::ota_manager; use crate::ota::ota_update::OTAPlatform; use crate::server::WebEnergyActor; use crate::server::custom_error::HttpResult; use actix_multipart::form::MultipartForm; use actix_multipart::form::tempfile::TempFile; use actix_web::{HttpResponse, web}; pub async fn supported_platforms() -> HttpResult { Ok(HttpResponse::Ok().json(OTAPlatform::supported_platforms())) } #[derive(Debug, MultipartForm)] pub struct UploadForm { #[multipart(rename = "firmware")] firmware: Vec, } #[derive(serde::Deserialize)] pub struct SpecificOTAVersionPath { platform: OTAPlatform, version: semver::Version, } /// Upload a new firmware update pub async fn upload_firmware( MultipartForm(form): MultipartForm, path: web::Path, ) -> HttpResult { if ota_manager::update_exists(path.platform, &path.version)? { return Ok(HttpResponse::Conflict() .json("A firmware with the same version has already been uploaded on the platform!")); } let Some(file) = form.firmware.first() else { return Ok(HttpResponse::BadRequest().json("No firmware specified!")); }; if file.size == 0 { return Ok(HttpResponse::BadRequest().json("Uploaded file is empty!")); } if file.size > constants::MAX_FIRMWARE_SIZE { return Ok(HttpResponse::BadRequest().json("Uploaded file is too heavy!")); } let content = std::fs::read(file.file.path())?; ota_manager::save_update(path.platform, &path.version, &content)?; Ok(HttpResponse::Accepted().body("OTA update successfully saved.")) } /// Download a firmware update pub async fn download_firmware(path: web::Path) -> HttpResult { if !ota_manager::update_exists(path.platform, &path.version)? { return Ok(HttpResponse::NotFound().json("The requested firmware update was not found!")); } let firmware = ota_manager::get_ota_update(path.platform, &path.version)?; Ok(HttpResponse::Ok() .content_type("application/octet-stream") .append_header(( "content-disposition", format!( "attachment; filename=\"{}-{}.bin\"", path.platform, path.version ), )) .body(firmware)) } /// Delete an uploaded firmware update pub async fn delete_update(path: web::Path) -> HttpResult { if !ota_manager::update_exists(path.platform, &path.version)? { return Ok(HttpResponse::NotFound().json("The requested firmware update was not found!")); } ota_manager::delete_update(path.platform, &path.version)?; Ok(HttpResponse::Accepted().finish()) } /// Get the list of all OTA updates pub async fn list_all_ota() -> HttpResult { Ok(HttpResponse::Ok().json(ota_manager::get_all_ota_updates()?)) } #[derive(serde::Deserialize)] pub struct ListOTAPath { platform: OTAPlatform, } /// List OTA software updates for a given platform pub async fn list_updates_platform(path: web::Path) -> HttpResult { let list = ota_manager::get_ota_updates_for_platform(path.platform)?; Ok(HttpResponse::Ok().json(list)) } #[derive(serde::Deserialize)] pub struct SetDesiredDeviceVersion { devices: Option>, platform: Option, version: semver::Version, } pub async fn set_desired_version( actor: WebEnergyActor, body: web::Json, ) -> HttpResult { if body.devices.is_none() && body.platform.is_none() { return Ok( HttpResponse::BadRequest().json("Must specify one filter to select target devices!") ); } let devices = actor.send(energy_actor::GetDeviceLists).await?; for d in devices { // Filter per platform if let Some(p) = body.platform { if d.info.reference != p.to_string() { continue; } } // Filter per device if let Some(ids) = &body.devices { if !ids.contains(&d.id) { continue; } } actor .send(energy_actor::SetDesiredVersion( d.id, Some(body.version.clone()), )) .await??; } Ok(HttpResponse::Ok().finish()) }