Can set device desired version
This commit is contained in:
		@@ -9,7 +9,7 @@ use std::collections::{HashMap, HashSet};
 | 
				
			|||||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
 | 
					#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
 | 
				
			||||||
pub struct DeviceInfo {
 | 
					pub struct DeviceInfo {
 | 
				
			||||||
    /// Device reference
 | 
					    /// Device reference
 | 
				
			||||||
    reference: String,
 | 
					    pub reference: String,
 | 
				
			||||||
    /// Device firmware / software version
 | 
					    /// Device firmware / software version
 | 
				
			||||||
    version: semver::Version,
 | 
					    version: semver::Version,
 | 
				
			||||||
    /// Maximum number of relay that the device can support
 | 
					    /// Maximum number of relay that the device can support
 | 
				
			||||||
@@ -62,6 +62,9 @@ pub struct Device {
 | 
				
			|||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// There cannot be more than [info.max_relays] relays
 | 
					    /// There cannot be more than [info.max_relays] relays
 | 
				
			||||||
    pub relays: Vec<DeviceRelay>,
 | 
					    pub relays: Vec<DeviceRelay>,
 | 
				
			||||||
 | 
					    /// Desired version, ie. the version of the software we would to seen run on the device
 | 
				
			||||||
 | 
					    #[serde(skip_serializing_if = "Option::is_none")]
 | 
				
			||||||
 | 
					    pub desired_version: Option<semver::Version>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Structure that contains information about the minimal expected execution
 | 
					/// Structure that contains information about the minimal expected execution
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -84,6 +84,7 @@ impl DevicesList {
 | 
				
			|||||||
            validated: false,
 | 
					            validated: false,
 | 
				
			||||||
            enabled: false,
 | 
					            enabled: false,
 | 
				
			||||||
            relays: vec![],
 | 
					            relays: vec![],
 | 
				
			||||||
 | 
					            desired_version: None,
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // First, write CSR
 | 
					        // First, write CSR
 | 
				
			||||||
@@ -186,6 +187,24 @@ impl DevicesList {
 | 
				
			|||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Set a device desired version
 | 
				
			||||||
 | 
					    pub fn set_desired_version(
 | 
				
			||||||
 | 
					        &mut self,
 | 
				
			||||||
 | 
					        id: &DeviceId,
 | 
				
			||||||
 | 
					        version: Option<semver::Version>,
 | 
				
			||||||
 | 
					    ) -> anyhow::Result<()> {
 | 
				
			||||||
 | 
					        let dev = self
 | 
				
			||||||
 | 
					            .0
 | 
				
			||||||
 | 
					            .get_mut(id)
 | 
				
			||||||
 | 
					            .ok_or(DevicesListError::UpdateDeviceFailedDeviceNotFound)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        dev.desired_version = version;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.persist_dev_config(id)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Get single certificate information
 | 
					    /// Get single certificate information
 | 
				
			||||||
    fn get_cert(&self, id: &DeviceId) -> anyhow::Result<X509> {
 | 
					    fn get_cert(&self, id: &DeviceId) -> anyhow::Result<X509> {
 | 
				
			||||||
        let dev = self
 | 
					        let dev = self
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -195,6 +195,27 @@ impl Handler<UpdateDeviceGeneralInfo> for EnergyActor {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Set device desired version
 | 
				
			||||||
 | 
					#[derive(Message)]
 | 
				
			||||||
 | 
					#[rtype(result = "anyhow::Result<()>")]
 | 
				
			||||||
 | 
					pub struct SetDesiredVersion(pub DeviceId, pub Option<semver::Version>);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Handler<SetDesiredVersion> for EnergyActor {
 | 
				
			||||||
 | 
					    type Result = anyhow::Result<()>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn handle(&mut self, msg: SetDesiredVersion, _ctx: &mut Context<Self>) -> Self::Result {
 | 
				
			||||||
 | 
					        log::info!(
 | 
				
			||||||
 | 
					            "Requested to update device desired version {:?} => {:#?}",
 | 
				
			||||||
 | 
					            &msg.0,
 | 
				
			||||||
 | 
					            &msg.1
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.devices.set_desired_version(&msg.0, msg.1)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Delete a device
 | 
					/// Delete a device
 | 
				
			||||||
#[derive(Message)]
 | 
					#[derive(Message)]
 | 
				
			||||||
#[rtype(result = "anyhow::Result<()>")]
 | 
					#[rtype(result = "anyhow::Result<()>")]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -189,11 +189,13 @@ pub async fn secure_server(energy_actor: EnergyActorAddr) -> anyhow::Result<()>
 | 
				
			|||||||
                "/web_api/ota/{platform}/{version}",
 | 
					                "/web_api/ota/{platform}/{version}",
 | 
				
			||||||
                web::post().to(ota_controller::upload_firmware),
 | 
					                web::post().to(ota_controller::upload_firmware),
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            // TODO : upload a new software update
 | 
					 | 
				
			||||||
            // TODO : list ota software update per platform
 | 
					            // TODO : list ota software update per platform
 | 
				
			||||||
            // TODO : download a OTA file
 | 
					            // TODO : download a OTA file
 | 
				
			||||||
            // TODO : delete an OTA file
 | 
					            // TODO : delete an OTA file
 | 
				
			||||||
            // TODO : deploy an update to a device
 | 
					            .route(
 | 
				
			||||||
 | 
					                "/web_api/ota/set_desired_version",
 | 
				
			||||||
 | 
					                web::post().to(ota_controller::set_desired_version),
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
            // Logging controller API
 | 
					            // Logging controller API
 | 
				
			||||||
            .route(
 | 
					            .route(
 | 
				
			||||||
                "/web_api/logging/logs",
 | 
					                "/web_api/logging/logs",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,10 @@
 | 
				
			|||||||
use crate::constants;
 | 
					use crate::constants;
 | 
				
			||||||
 | 
					use crate::devices::device::DeviceId;
 | 
				
			||||||
 | 
					use crate::energy::energy_actor;
 | 
				
			||||||
use crate::ota::ota_manager;
 | 
					use crate::ota::ota_manager;
 | 
				
			||||||
use crate::ota::ota_update::OTAPlatform;
 | 
					use crate::ota::ota_update::OTAPlatform;
 | 
				
			||||||
use crate::server::custom_error::HttpResult;
 | 
					use crate::server::custom_error::HttpResult;
 | 
				
			||||||
 | 
					use crate::server::WebEnergyActor;
 | 
				
			||||||
use actix_multipart::form::tempfile::TempFile;
 | 
					use actix_multipart::form::tempfile::TempFile;
 | 
				
			||||||
use actix_multipart::form::MultipartForm;
 | 
					use actix_multipart::form::MultipartForm;
 | 
				
			||||||
use actix_web::{web, HttpResponse};
 | 
					use actix_web::{web, HttpResponse};
 | 
				
			||||||
@@ -49,3 +52,48 @@ pub async fn upload_firmware(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    Ok(HttpResponse::Accepted().body("OTA update successfully saved."))
 | 
					    Ok(HttpResponse::Accepted().body("OTA update successfully saved."))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(serde::Deserialize)]
 | 
				
			||||||
 | 
					pub struct SetDesiredDeviceVersion {
 | 
				
			||||||
 | 
					    devices: Option<Vec<DeviceId>>,
 | 
				
			||||||
 | 
					    platform: Option<OTAPlatform>,
 | 
				
			||||||
 | 
					    version: semver::Version,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub async fn set_desired_version(
 | 
				
			||||||
 | 
					    actor: WebEnergyActor,
 | 
				
			||||||
 | 
					    body: web::Json<SetDesiredDeviceVersion>,
 | 
				
			||||||
 | 
					) -> 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())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -37,6 +37,7 @@ export interface Device {
 | 
				
			|||||||
  validated: boolean;
 | 
					  validated: boolean;
 | 
				
			||||||
  enabled: boolean;
 | 
					  enabled: boolean;
 | 
				
			||||||
  relays: DeviceRelay[];
 | 
					  relays: DeviceRelay[];
 | 
				
			||||||
 | 
					  desired_version?: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface UpdatedInfo {
 | 
					export interface UpdatedInfo {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -41,6 +41,10 @@ export function GeneralDeviceInfo(p: {
 | 
				
			|||||||
              value={p.device.info.reference}
 | 
					              value={p.device.info.reference}
 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
            <DeviceInfoProperty label="Version" value={p.device.info.version} />
 | 
					            <DeviceInfoProperty label="Version" value={p.device.info.version} />
 | 
				
			||||||
 | 
					            <DeviceInfoProperty
 | 
				
			||||||
 | 
					              label="Desired version"
 | 
				
			||||||
 | 
					              value={p.device.desired_version ?? "None"}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
            <DeviceInfoProperty label="Name" value={p.device.name} />
 | 
					            <DeviceInfoProperty label="Name" value={p.device.name} />
 | 
				
			||||||
            <DeviceInfoProperty
 | 
					            <DeviceInfoProperty
 | 
				
			||||||
              label="Description"
 | 
					              label="Description"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,4 +14,5 @@ Upload firmware to central backend, in dev mode:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
```bash
 | 
					```bash
 | 
				
			||||||
curl -k -X POST  https://localhost:8443/web_api/ota/Wt32-Eth01/$(cat version.txt) --form firmware="@build/main.bin"
 | 
					curl -k -X POST  https://localhost:8443/web_api/ota/Wt32-Eth01/$(cat version.txt) --form firmware="@build/main.bin"
 | 
				
			||||||
 | 
					curl -k -X POST  https://localhost:8443/web_api/ota/set_desired_version --header "Content-Type: application/json" --data "{\"platform\": \"Wt32-Eth01\", \"version\": \"$(cat version.txt)\"}"
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
							
								
								
									
										5
									
								
								esp32_device/build_upload_dev.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										5
									
								
								esp32_device/build_upload_dev.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					#!/bin/sh
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					idf.py build && \
 | 
				
			||||||
 | 
					    curl -k -X POST  https://localhost:8443/web_api/ota/Wt32-Eth01/$(cat version.txt) --form firmware="@build/main.bin" && \
 | 
				
			||||||
 | 
					    curl -k -X POST  https://localhost:8443/web_api/ota/set_desired_version --header "Content-Type: application/json" --data "{\"platform\": \"Wt32-Eth01\", \"version\": \"$(cat version.txt)\"}"
 | 
				
			||||||
		Reference in New Issue
	
	Block a user