Compare commits
	
		
			174 Commits
		
	
	
		
			0fdfb43e2a
			...
			1.0.3
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 4d46f9c52c | |||
| c90a05fcfd | |||
| abdca20a66 | |||
| 88a24565b4 | |||
| 3625188706 | |||
| 58eceeda2d | |||
| c44c6c3bf5 | |||
| 4ef714fdbd | |||
| bab4525908 | |||
| 2ed4299032 | |||
| bcc92da065 | |||
| af83130b63 | |||
| 950b576373 | |||
| 2459b0cf99 | |||
| 814204b4ce | |||
| de8fcfbcfb | |||
| 49a7011d84 | |||
| 39aff8054a | |||
| 850f828dca | |||
| 59383debd2 | |||
| 58cf0a9614 | |||
| f30aaf71dd | |||
| 27ce9e9a96 | |||
| 903dd104f3 | |||
| 205981fbc8 | |||
| 623b0a4671 | |||
| fd25e71cf8 | |||
| 842b48e782 | |||
| e8374a8ef7 | |||
| 347c247285 | |||
| c59aeed4ab | |||
| b7f1beb1b7 | |||
| 4bd700c2db | |||
| 3f1c5e4ac0 | |||
| f77506dc46 | |||
| 5b7554b6bf | |||
| 8fdbcc4f3a | |||
| 9f4c2b0e35 | |||
| 0a7138a82b | |||
| 2ce09a94b1 | |||
| 355b2a71ce | |||
| 6b04bf4261 | |||
| 99c6963210 | |||
| 89cfd3ce21 | |||
| 0c40ff2750 | |||
| 75be1ed1d2 | |||
| 6a2baca3f2 | |||
| 4d0d20b424 | |||
| 05cf488be7 | |||
| b8ecc83668 | |||
| c1912717e4 | |||
| a8780e60d3 | |||
| 5005cf84f9 | |||
| 561992010c | |||
| 4725e67ee1 | |||
| fbb55628be | |||
| 79e49ed5a9 | |||
| 138b9d2dbe | |||
| 3413a1ee21 | |||
| 64055568e0 | |||
| af71b574cc | |||
| 6088237b79 | |||
| 05a033b51c | |||
| 3d3ccf5242 | |||
| 68521c238c | |||
| f03278f8c5 | |||
| a63d1f17de | |||
| bb6d8a8be1 | |||
| 693414b1eb | |||
| f74e86a8db | |||
| 53d4cb1de7 | |||
| 498ca3925a | |||
| 155806df78 | |||
| 79f3668021 | |||
| 660e6e8a5b | |||
| 0e3182434f | |||
| cf2f034e6c | |||
| ddd27519a9 | |||
| e8d2e8b318 | |||
| 0dfc25a918 | |||
| 3078b3c645 | |||
| 9d3bed68af | |||
| 9fe00f149c | |||
| 65164055d6 | |||
| 08680122f2 | |||
| 642366540e | |||
| 482da63a3b | |||
| cade9dc02b | |||
| 9b87025b27 | |||
| 46d3f3580c | |||
| bda9f6a9c9 | |||
| 41380a103f | |||
| 2573778b82 | |||
| 527126d926 | |||
| 9a1104c57b | |||
| bf3e703bb1 | |||
| d7607bc483 | |||
| 306be8272a | |||
| 742bb8e2ed | |||
| e10d7ef478 | |||
| 18ba6c60d6 | |||
| d70562f97a | |||
| b0d192a9aa | |||
| 99f6b17f4e | |||
| 3fff4d624e | |||
| e2f6212a75 | |||
| bff1c88da8 | |||
| 7933c2dae3 | |||
| 15b8a69700 | |||
| 157c77142a | |||
| 25f6e808ed | |||
| 6f298d3238 | |||
| b75e868cc3 | |||
| 6a2b5e320d | |||
| 52637fc401 | |||
| 8ea9da9443 | |||
| b1b4eaa341 | |||
| 94652a82fe | |||
| 832ab86536 | |||
| 32dbbf4678 | |||
| 1f48203564 | |||
| 0b6526f901 | |||
| 741db9e13b | |||
| 061ae8c208 | |||
| 4a68800907 | |||
| 6738d47507 | |||
| acbce81b46 | |||
| 1615531d67 | |||
| 5355b351d1 | |||
| ad10df3e7f | |||
| edf70cb8fc | |||
| 8af3018b34 | |||
| dd96cc4bf3 | |||
| 9e9a227332 | |||
| d701c406d6 | |||
| 74915f109d | |||
| 40af167c1a | |||
| 124fc71c9a | |||
| ac6f93dd4a | |||
| 695fa1fcf4 | |||
| 3131c757a9 | |||
| 60d5be58e1 | |||
| 4b90e0ede1 | |||
| d351aebce4 | |||
| 24d95fff89 | |||
| 11e536fee0 | |||
| e2328b47e5 | |||
| bc790df8e6 | |||
| 104e4f2c15 | |||
| 709671c35d | |||
| e769b2d83b | |||
| 33c3769818 | |||
| 7c5eeaeaa0 | |||
| bcdb72e217 | |||
| 7173d206f2 | |||
| 74a8d3ffbf | |||
| 31bc7898ee | |||
| 27a6d8f7ed | |||
| 43c988b80e | |||
| 9531281fd2 | |||
| e589e2e80c | |||
| 36c127a9c9 | |||
| f0b9683519 | |||
| 2e793770f1 | |||
| ceac6a2683 | |||
| f232a63496 | |||
| adfe006270 | |||
| aa6ca82edd | |||
| d939752aed | |||
| ca6532aee3 | |||
| bd5fc18bab | |||
| 80023ddee3 | |||
| d48094aca6 | |||
| 47e59b7035 | 
							
								
								
									
										37
									
								
								.drone.yml
									
									
									
									
									
								
							
							
						
						
									
										37
									
								
								.drone.yml
									
									
									
									
									
								
							| @@ -46,6 +46,11 @@ steps: | |||||||
|         path: /usr/local/cargo/registry |         path: /usr/local/cargo/registry | ||||||
|       - name: web_app |       - name: web_app | ||||||
|         path: /tmp/web_build |         path: /tmp/web_build | ||||||
|  |       - name: releases | ||||||
|  |         path: /tmp/releases | ||||||
|  |     when: | ||||||
|  |       event: | ||||||
|  |         - tag | ||||||
|     depends_on: |     depends_on: | ||||||
|       - backend_check |       - backend_check | ||||||
|       - web_build |       - web_build | ||||||
| @@ -54,13 +59,41 @@ steps: | |||||||
|       - mv /tmp/web_build/dist static |       - mv /tmp/web_build/dist static | ||||||
|       - cargo build --release |       - cargo build --release | ||||||
|       - ls -lah target/release/central_backend |       - ls -lah target/release/central_backend | ||||||
|  |       - mv target/release/central_backend /tmp/releases/central_backend | ||||||
|  |  | ||||||
|  |   # Build ESP32 program | ||||||
|   - name: esp32_compile |   - name: esp32_compile | ||||||
|     image: espressif/idf:v5.4.2 |     image: espressif/idf:v5.5.1 | ||||||
|  |     volumes: | ||||||
|  |       - name: releases | ||||||
|  |         path: /tmp/releases | ||||||
|     commands: |     commands: | ||||||
|       - cd esp32_device |       - cd esp32_device | ||||||
|       - /opt/esp/entrypoint.sh idf.py build |       - /opt/esp/entrypoint.sh idf.py build | ||||||
|       - ls -lah build/main.bin |       - ls -lah build/main.bin | ||||||
|  |       - cp build/main.bin /tmp/releases/wt32-eth01.bin | ||||||
|  |  | ||||||
|  |   # Auto-release to Gitea | ||||||
|  |   - name: gitea_release | ||||||
|  |     image: plugins/gitea-release | ||||||
|  |     depends_on: | ||||||
|  |     - backend_compile | ||||||
|  |     - esp32_compile | ||||||
|  |     when: | ||||||
|  |       event: | ||||||
|  |         - tag | ||||||
|  |     volumes: | ||||||
|  |       - name: releases | ||||||
|  |         path: /tmp/releases | ||||||
|  |     environment: | ||||||
|  |       PLUGIN_API_KEY: | ||||||
|  |         from_secret: GITEA_API_KEY # needs permission write:repository | ||||||
|  |     settings: | ||||||
|  |       base_url: https://gitea.communiquons.org | ||||||
|  |       files: | ||||||
|  |         - /tmp/releases/central_backend | ||||||
|  |         - /tmp/releases/wt32-eth01.bin | ||||||
|  |       checksum: sha512 | ||||||
|  |  | ||||||
|  |  | ||||||
| volumes: | volumes: | ||||||
| @@ -68,3 +101,5 @@ volumes: | |||||||
|     temp: {} |     temp: {} | ||||||
|   - name: web_app |   - name: web_app | ||||||
|     temp: {} |     temp: {} | ||||||
|  |   - name: releases | ||||||
|  |     temp: {} | ||||||
|   | |||||||
							
								
								
									
										707
									
								
								central_backend/Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										707
									
								
								central_backend/Cargo.lock
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,46 +1,46 @@ | |||||||
| [package] | [package] | ||||||
| name = "central_backend" | name = "central_backend" | ||||||
| version = "1.0.2" | version = "1.0.3" | ||||||
| edition = "2024" | edition = "2024" | ||||||
|  |  | ||||||
| [dependencies] | [dependencies] | ||||||
| log = "0.4.27" | log = "0.4.28" | ||||||
| env_logger = "0.11.8" | env_logger = "0.11.8" | ||||||
| lazy_static = "1.5.0" | lazy_static = "1.5.0" | ||||||
| dotenvy = "0.15.7" | dotenvy = "0.15.7" | ||||||
| clap = { version = "4.5.41", features = ["derive", "env"] } | clap = { version = "4.5.50", features = ["derive", "env"] } | ||||||
| anyhow = "1.0.98" | anyhow = "1.0.100" | ||||||
| thiserror = "2.0.12" | thiserror = "2.0.17" | ||||||
| openssl = { version = "0.10.73" } | openssl = { version = "0.10.74" } | ||||||
| openssl-sys = "0.9.109" | openssl-sys = "0.9.110" | ||||||
| libc = "0.2.174" | libc = "0.2.177" | ||||||
| foreign-types-shared = "0.1.1" | foreign-types-shared = "0.1.1" | ||||||
| asn1 = "0.22.0" | asn1 = "0.23.0" | ||||||
| actix-web = { version = "4.11.0", features = ["openssl"] } | actix-web = { version = "4.11.0", features = ["openssl"] } | ||||||
| futures = "0.3.31" | futures = "0.3.31" | ||||||
| serde = { version = "1.0.219", features = ["derive"] } | serde = { version = "1.0.228", features = ["derive"] } | ||||||
| reqwest = { version = "0.12.22", features = ["json"] } | reqwest = { version = "0.12.24", features = ["json"] } | ||||||
| serde_json = "1.0.141" | serde_json = "1.0.145" | ||||||
| rand = "0.9.2" | rand = "0.10.0-rc.0" | ||||||
| actix = "0.13.5" | actix = "0.13.5" | ||||||
| actix-identity = "0.8.0" | actix-identity = "0.9.0" | ||||||
| actix-session = { version = "0.10.1", features = ["cookie-session"] } | actix-session = { version = "0.11.0", features = ["cookie-session"] } | ||||||
| actix-cors = "0.7.1" | actix-cors = "0.7.1" | ||||||
| actix-multipart = { version = "0.7.2", features = ["derive"] } | actix-multipart = { version = "0.7.2", features = ["derive"] } | ||||||
| actix-remote-ip = "0.1.0" | actix-remote-ip = "0.1.0" | ||||||
| futures-util = "0.3.31" | futures-util = "0.3.31" | ||||||
| uuid = { version = "1.16.0", features = ["v4", "serde"] } | uuid = { version = "1.18.1", features = ["v4", "serde"] } | ||||||
| semver = { version = "1.0.26", features = ["serde"] } | semver = { version = "1.0.27", features = ["serde"] } | ||||||
| lazy-regex = "3.4.1" | lazy-regex = "3.4.1" | ||||||
| tokio = { version = "1.44.2", features = ["full"] } | tokio = { version = "1.48.0", features = ["full"] } | ||||||
| tokio_schedule = "0.3.2" | tokio_schedule = "0.3.2" | ||||||
| mime_guess = "2.0.5" | mime_guess = "2.0.5" | ||||||
| rust-embed = "8.6.0" | rust-embed = "8.8.0" | ||||||
| jsonwebtoken = { version = "9.3.1", features = ["use_pem"] } | jsonwebtoken = { version = "10.1.0", features = ["use_pem", "rust_crypto"] } | ||||||
| prettytable-rs = "0.10.0" | prettytable-rs = "0.10.0" | ||||||
| chrono = "0.4.41" | chrono = "0.4.42" | ||||||
| serde_yml = "0.0.12" | serde_yml = "0.0.12" | ||||||
| bincode = "2.0.1" | bincode = "2.0.1" | ||||||
| fs4 = { version = "0.13.1", features = ["sync"] } | fs4 = { version = "0.13.1", features = ["sync"] } | ||||||
| zip = { version = "2.2.0", features = ["bzip2"] } | zip = { version = "6.0.0", features = ["bzip2"] } | ||||||
| walkdir = "2.5.0" | walkdir = "2.5.0" | ||||||
|   | |||||||
							
								
								
									
										33
									
								
								central_backend/engine_test/test_turn_forced_off.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								central_backend/engine_test/test_turn_forced_off.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | |||||||
|  | devices: | ||||||
|  |   - id: dev1 | ||||||
|  |     info: | ||||||
|  |       reference: A | ||||||
|  |       version: 0.0.1 | ||||||
|  |       max_relays: 1 | ||||||
|  |     time_create: 1 | ||||||
|  |     time_update: 1 | ||||||
|  |     name: Dev1 | ||||||
|  |     description: Day1 | ||||||
|  |     validated: true | ||||||
|  |     enabled: true | ||||||
|  |     relays: | ||||||
|  |       - id: dcb3fd91-bf9b-4de3-99e5-92c1c7dd72e9 | ||||||
|  |         name: R1 | ||||||
|  |         enabled: true | ||||||
|  |         priority: 1 | ||||||
|  |         consumption: 100 | ||||||
|  |         minimal_uptime: 10 | ||||||
|  |         minimal_downtime: 10 | ||||||
|  |         depends_on: [] | ||||||
|  |         conflicts_with: [] | ||||||
|  |  | ||||||
|  |         on: false | ||||||
|  |         for: 5000 | ||||||
|  |         forced_state: | ||||||
|  |           type: Off | ||||||
|  |           for_secs: 500 | ||||||
|  |         should_be_on: false | ||||||
|  |  | ||||||
|  |     online: true | ||||||
|  |  | ||||||
|  | curr_consumption: -10000 | ||||||
							
								
								
									
										49
									
								
								central_backend/engine_test/test_turn_forced_on.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								central_backend/engine_test/test_turn_forced_on.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,49 @@ | |||||||
|  | devices: | ||||||
|  |   - id: dev1 | ||||||
|  |     info: | ||||||
|  |       reference: A | ||||||
|  |       version: 0.0.1 | ||||||
|  |       max_relays: 1 | ||||||
|  |     time_create: 1 | ||||||
|  |     time_update: 1 | ||||||
|  |     name: Dev1 | ||||||
|  |     description: Day1 | ||||||
|  |     validated: true | ||||||
|  |     enabled: true | ||||||
|  |     relays: | ||||||
|  |       - id: dcb3fd91-bf9b-4de3-99e5-92c1c7dd72e9 | ||||||
|  |         name: R1 | ||||||
|  |         enabled: true | ||||||
|  |         priority: 1 | ||||||
|  |         consumption: 100 | ||||||
|  |         minimal_uptime: 10 | ||||||
|  |         minimal_downtime: 10 | ||||||
|  |         depends_on: [] | ||||||
|  |         conflicts_with: [] | ||||||
|  |  | ||||||
|  |         on: false | ||||||
|  |         for: 500 | ||||||
|  |         forced_state: | ||||||
|  |           type: On | ||||||
|  |           for_secs: 500 | ||||||
|  |         should_be_on: true | ||||||
|  |  | ||||||
|  |       - id: dcb3fd91-bf9b-4de3-99e5-92c1c7dd72f0 | ||||||
|  |         name: R2 | ||||||
|  |         enabled: true | ||||||
|  |         priority: 1 | ||||||
|  |         consumption: 100 | ||||||
|  |         minimal_uptime: 10 | ||||||
|  |         minimal_downtime: 10 | ||||||
|  |         depends_on: [ ] | ||||||
|  |         conflicts_with: [ ] | ||||||
|  |  | ||||||
|  |         on: false | ||||||
|  |         for: 500 | ||||||
|  |         forced_state: | ||||||
|  |           type: None | ||||||
|  |         should_be_on: false | ||||||
|  |  | ||||||
|  |     online: true | ||||||
|  |  | ||||||
|  | curr_consumption: 10000 | ||||||
| @@ -16,11 +16,13 @@ impl CRLDistributionPointExt { | |||||||
|         let crl_bytes = asn1::write(|w| { |         let crl_bytes = asn1::write(|w| { | ||||||
|             w.write_element(&asn1::SequenceWriter::new(&|w| { |             w.write_element(&asn1::SequenceWriter::new(&|w| { | ||||||
|                 w.write_element(&asn1::SequenceWriter::new(&|w| { |                 w.write_element(&asn1::SequenceWriter::new(&|w| { | ||||||
|                     w.write_tlv(tag_a0, |w| { |                     w.write_tlv(tag_a0, None, |w: &mut asn1::WriteBuf| { | ||||||
|                         w.push_slice(&asn1::write(|w| { |                         w.push_slice(&asn1::write(|w| { | ||||||
|                             w.write_tlv(tag_a0, |w| { |                             w.write_tlv(tag_a0, None, |w: &mut asn1::WriteBuf| { | ||||||
|                                 w.push_slice(&asn1::write(|w| { |                                 w.push_slice(&asn1::write(|w| { | ||||||
|                                     w.write_tlv(tag_86, |b| b.push_slice(self.url.as_bytes()))?; |                                     w.write_tlv(tag_86, None, |b| { | ||||||
|  |                                         b.push_slice(self.url.as_bytes()) | ||||||
|  |                                     })?; | ||||||
|                                     Ok(()) |                                     Ok(()) | ||||||
|                                 })?) |                                 })?) | ||||||
|                             })?; |                             })?; | ||||||
|   | |||||||
| @@ -174,17 +174,16 @@ fn gen_certificate(req: GenCertificateReq) -> anyhow::Result<(Option<Vec<u8>>, V | |||||||
|     cert_builder.set_not_after(¬_after)?; |     cert_builder.set_not_after(¬_after)?; | ||||||
|  |  | ||||||
|     // Specify CRL URL |     // Specify CRL URL | ||||||
|     if let Some(issuer) = req.issuer { |     if let Some(issuer) = req.issuer | ||||||
|         if let Some(crl) = &issuer.crl { |         && let Some(crl) = &issuer.crl | ||||||
|             let crl_url = format!( |     { | ||||||
|                 "{}/pki/{}", |         let crl_url = format!( | ||||||
|                 AppConfig::get().unsecure_origin(), |             "{}/pki/{}", | ||||||
|                 crl.file_name().unwrap().to_string_lossy() |             AppConfig::get().unsecure_origin(), | ||||||
|             ); |             crl.file_name().unwrap().to_string_lossy() | ||||||
|  |         ); | ||||||
|  |  | ||||||
|             cert_builder |         cert_builder.append_extension(CRLDistributionPointExt { url: crl_url }.as_extension()?)?; | ||||||
|                 .append_extension(CRLDistributionPointExt { url: crl_url }.as_extension()?)?; |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // If cert is a CA or not |     // If cert is a CA or not | ||||||
| @@ -424,12 +423,12 @@ fn refresh_crl(d: &CertData, new_cert: Option<&X509>) -> anyhow::Result<()> { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         // Add old entries |         // Add old entries | ||||||
|         if let Some(old_crl) = old_crl { |         if let Some(old_crl) = old_crl | ||||||
|             if let Some(entries) = old_crl.get_revoked() { |             && let Some(entries) = old_crl.get_revoked() | ||||||
|                 for entry in entries { |         { | ||||||
|                     if X509_CRL_add0_revoked(crl, X509_REVOKED_dup(entry.as_ptr())) == 0 { |             for entry in entries { | ||||||
|                         return Err(PKIError::GenCRLError("X509_CRL_add0_revoked").into()); |                 if X509_CRL_add0_revoked(crl, X509_REVOKED_dup(entry.as_ptr())) == 0 { | ||||||
|                     } |                     return Err(PKIError::GenCRLError("X509_CRL_add0_revoked").into()); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ use crate::energy::consumption; | |||||||
| use crate::energy::consumption::EnergyConsumption; | use crate::energy::consumption::EnergyConsumption; | ||||||
| use crate::energy::consumption_cache::ConsumptionCache; | use crate::energy::consumption_cache::ConsumptionCache; | ||||||
| use crate::energy::consumption_history_file::ConsumptionHistoryFile; | use crate::energy::consumption_history_file::ConsumptionHistoryFile; | ||||||
| use crate::energy::engine::EnergyEngine; | use crate::energy::engine::{EnergyEngine, RelayForcedState}; | ||||||
| use crate::utils::time_utils::time_secs; | use crate::utils::time_utils::time_secs; | ||||||
| use actix::prelude::*; | use actix::prelude::*; | ||||||
| use openssl::x509::X509Req; | use openssl::x509::X509Req; | ||||||
| @@ -328,6 +328,19 @@ impl Handler<UpdateDeviceRelay> for EnergyActor { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #[derive(Message)] | ||||||
|  | #[rtype(result = "anyhow::Result<()>")] | ||||||
|  | pub struct SetRelayForcedState(pub DeviceRelayID, pub RelayForcedState); | ||||||
|  |  | ||||||
|  | impl Handler<SetRelayForcedState> for EnergyActor { | ||||||
|  |     type Result = anyhow::Result<()>; | ||||||
|  |  | ||||||
|  |     fn handle(&mut self, msg: SetRelayForcedState, _ctx: &mut Context<Self>) -> Self::Result { | ||||||
|  |         self.engine.relay_state(msg.0).set_forced(msg.1); | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| /// Delete a device relay | /// Delete a device relay | ||||||
| #[derive(Message)] | #[derive(Message)] | ||||||
| #[rtype(result = "anyhow::Result<()>")] | #[rtype(result = "anyhow::Result<()>")] | ||||||
| @@ -408,6 +421,7 @@ pub struct ResRelayState { | |||||||
|     pub id: DeviceRelayID, |     pub id: DeviceRelayID, | ||||||
|     pub on: bool, |     pub on: bool, | ||||||
|     pub r#for: usize, |     pub r#for: usize, | ||||||
|  |     pub forced_state: RelayForcedState, | ||||||
| } | } | ||||||
|  |  | ||||||
| /// Get the state of all relays | /// Get the state of all relays | ||||||
| @@ -427,6 +441,7 @@ impl Handler<GetAllRelaysState> for EnergyActor { | |||||||
|                 id: d.id, |                 id: d.id, | ||||||
|                 on: state.is_on(), |                 on: state.is_on(), | ||||||
|                 r#for: state.state_for(), |                 r#for: state.state_for(), | ||||||
|  |                 forced_state: state.actual_forced_state(), | ||||||
|             }) |             }) | ||||||
|         } |         } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -25,19 +25,83 @@ impl DeviceState { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #[derive(Default, Debug, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize)] | ||||||
|  | #[serde(tag = "type")] | ||||||
|  | pub enum SetRelayForcedStateReq { | ||||||
|  |     #[default] | ||||||
|  |     None, | ||||||
|  |     Off { | ||||||
|  |         for_secs: u64, | ||||||
|  |     }, | ||||||
|  |     On { | ||||||
|  |         for_secs: u64, | ||||||
|  |     }, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl SetRelayForcedStateReq { | ||||||
|  |     pub fn to_forced_state(&self) -> RelayForcedState { | ||||||
|  |         match &self { | ||||||
|  |             SetRelayForcedStateReq::None => RelayForcedState::None, | ||||||
|  |             SetRelayForcedStateReq::Off { for_secs } => RelayForcedState::Off { | ||||||
|  |                 until: time_secs() + for_secs, | ||||||
|  |             }, | ||||||
|  |             SetRelayForcedStateReq::On { for_secs } => RelayForcedState::On { | ||||||
|  |                 until: time_secs() + for_secs, | ||||||
|  |             }, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[derive(Default, Debug, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize)] | ||||||
|  | #[serde(tag = "type")] | ||||||
|  | pub enum RelayForcedState { | ||||||
|  |     #[default] | ||||||
|  |     None, | ||||||
|  |     Off { | ||||||
|  |         until: u64, | ||||||
|  |     }, | ||||||
|  |     On { | ||||||
|  |         until: u64, | ||||||
|  |     }, | ||||||
|  | } | ||||||
|  |  | ||||||
| #[derive(Default, Clone)] | #[derive(Default, Clone)] | ||||||
| pub struct RelayState { | pub struct RelayState { | ||||||
|     on: bool, |     on: bool, | ||||||
|     since: usize, |     since: usize, | ||||||
|  |     forced_state: RelayForcedState, | ||||||
| } | } | ||||||
|  |  | ||||||
| impl RelayState { | impl RelayState { | ||||||
|  |     /// Get actual forced state (returns None if state is expired) | ||||||
|  |     pub fn actual_forced_state(&self) -> RelayForcedState { | ||||||
|  |         match self.forced_state { | ||||||
|  |             RelayForcedState::Off { until } if until > time_secs() => { | ||||||
|  |                 RelayForcedState::Off { until } | ||||||
|  |             } | ||||||
|  |             RelayForcedState::On { until } if until > time_secs() => RelayForcedState::On { until }, | ||||||
|  |             _ => RelayForcedState::None, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     pub fn is_on(&self) -> bool { |     pub fn is_on(&self) -> bool { | ||||||
|         self.on |         let forced_state = self.actual_forced_state(); | ||||||
|  |         (self.on || matches!(forced_state, RelayForcedState::On { .. })) | ||||||
|  |             && !matches!(forced_state, RelayForcedState::Off { .. }) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn is_off(&self) -> bool { |     fn is_off(&self) -> bool { | ||||||
|         !self.on |         !self.is_on() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// Check if relay state is enforced | ||||||
|  |     pub fn is_forced(&self) -> bool { | ||||||
|  |         self.actual_forced_state() != RelayForcedState::None | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub fn set_forced(&mut self, s: RelayForcedState) { | ||||||
|  |         self.since = time_secs() as usize; | ||||||
|  |         self.forced_state = s; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pub fn state_for(&self) -> usize { |     pub fn state_for(&self) -> usize { | ||||||
| @@ -146,7 +210,11 @@ impl EnergyEngine { | |||||||
|                     r.name, |                     r.name, | ||||||
|                     r.consumption, |                     r.consumption, | ||||||
|                     format!("{} / {}", r.minimal_downtime, r.minimal_uptime), |                     format!("{} / {}", r.minimal_downtime, r.minimal_uptime), | ||||||
|                     status.is_on().to_string(), |                     status.is_on().to_string() | ||||||
|  |                         + match status.is_forced() { | ||||||
|  |                             true => " (Forced)", | ||||||
|  |                             false => "", | ||||||
|  |                         }, | ||||||
|                     status.since, |                     status.since, | ||||||
|                     match dev_online { |                     match dev_online { | ||||||
|                         true => "Online", |                         true => "Online", | ||||||
| @@ -192,19 +260,28 @@ impl EnergyEngine { | |||||||
|  |  | ||||||
|         let mut new_relays_state = self.relays_state.clone(); |         let mut new_relays_state = self.relays_state.clone(); | ||||||
|  |  | ||||||
|         // Forcefully turn off relays that belongs to offline devices |         // Forcefully turn off disabled relays | ||||||
|         for d in devices { |         for d in devices { | ||||||
|             if !self.device_state(&d.id).is_online() { |             for r in &d.relays { | ||||||
|                 for r in &d.relays { |                 if !r.enabled || !d.enabled { | ||||||
|                     new_relays_state.get_mut(&r.id).unwrap().on = false; |                     new_relays_state.get_mut(&r.id).unwrap().on = false; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // Forcefully turn off disabled relays |         // Apply forced relays state | ||||||
|         for d in devices { |         for d in devices { | ||||||
|             for r in &d.relays { |             for r in &d.relays { | ||||||
|                 if !r.enabled || !d.enabled { |                 if self.relay_state(r.id).is_forced() { | ||||||
|  |                     new_relays_state.get_mut(&r.id).unwrap().on = self.relay_state(r.id).is_on(); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // Forcefully turn off relays that belongs to offline devices | ||||||
|  |         for d in devices { | ||||||
|  |             if !self.device_state(&d.id).is_online() { | ||||||
|  |                 for r in &d.relays { | ||||||
|                     new_relays_state.get_mut(&r.id).unwrap().on = false; |                     new_relays_state.get_mut(&r.id).unwrap().on = false; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| @@ -216,7 +293,9 @@ impl EnergyEngine { | |||||||
|  |  | ||||||
|             for d in devices { |             for d in devices { | ||||||
|                 for r in &d.relays { |                 for r in &d.relays { | ||||||
|                     if new_relays_state.get(&r.id).unwrap().is_off() { |                     if new_relays_state.get(&r.id).unwrap().is_off() | ||||||
|  |                         || new_relays_state.get(&r.id).unwrap().is_forced() | ||||||
|  |                     { | ||||||
|                         continue; |                         continue; | ||||||
|                     } |                     } | ||||||
|  |  | ||||||
| @@ -240,7 +319,7 @@ impl EnergyEngine { | |||||||
|             for d in devices { |             for d in devices { | ||||||
|                 for r in &d.relays { |                 for r in &d.relays { | ||||||
|                     let state = new_relays_state.get(&r.id).unwrap(); |                     let state = new_relays_state.get(&r.id).unwrap(); | ||||||
|                     if state.is_off() { |                     if state.is_off() || state.is_forced() { | ||||||
|                         continue; |                         continue; | ||||||
|                     } |                     } | ||||||
|  |  | ||||||
| @@ -271,7 +350,9 @@ impl EnergyEngine { | |||||||
|                     continue; |                     continue; | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 if new_relays_state.get(&r.id).unwrap().is_on() { |                 if new_relays_state.get(&r.id).unwrap().is_on() | ||||||
|  |                     || new_relays_state.get(&r.id).unwrap().is_forced() | ||||||
|  |                 { | ||||||
|                     continue; |                     continue; | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
| @@ -298,7 +379,7 @@ impl EnergyEngine { | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // Order relays |         // Order relays to select the ones with the most elevated priorities | ||||||
|         let mut ordered_relays = devices |         let mut ordered_relays = devices | ||||||
|             .iter() |             .iter() | ||||||
|             .filter(|d| self.device_state(&d.id).is_online() && d.enabled) |             .filter(|d| self.device_state(&d.id).is_online() && d.enabled) | ||||||
| @@ -308,10 +389,13 @@ impl EnergyEngine { | |||||||
|         ordered_relays.sort_by_key(|r| r.priority); |         ordered_relays.sort_by_key(|r| r.priority); | ||||||
|         ordered_relays.reverse(); |         ordered_relays.reverse(); | ||||||
|  |  | ||||||
|  |         // Select relays to start, starting with those with highest priorities | ||||||
|         loop { |         loop { | ||||||
|             let mut changed = false; |             let mut changed = false; | ||||||
|             for relay in &ordered_relays { |             for relay in &ordered_relays { | ||||||
|                 if new_relays_state.get(&relay.id).unwrap().is_on() { |                 if new_relays_state.get(&relay.id).unwrap().is_on() | ||||||
|  |                     || new_relays_state.get(&relay.id).unwrap().is_forced() | ||||||
|  |                 { | ||||||
|                     continue; |                     continue; | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
| @@ -383,7 +467,7 @@ impl EnergyEngine { | |||||||
| mod test { | mod test { | ||||||
|     use crate::devices::device::{Device, DeviceId, DeviceRelayID}; |     use crate::devices::device::{Device, DeviceId, DeviceRelayID}; | ||||||
|     use crate::energy::consumption::EnergyConsumption; |     use crate::energy::consumption::EnergyConsumption; | ||||||
|     use crate::energy::engine::EnergyEngine; |     use crate::energy::engine::{EnergyEngine, SetRelayForcedStateReq}; | ||||||
|     use crate::utils::time_utils::time_secs; |     use crate::utils::time_utils::time_secs; | ||||||
|     use rust_embed::Embed; |     use rust_embed::Embed; | ||||||
|  |  | ||||||
| @@ -392,6 +476,8 @@ mod test { | |||||||
|         id: DeviceRelayID, |         id: DeviceRelayID, | ||||||
|         on: bool, |         on: bool, | ||||||
|         r#for: usize, |         r#for: usize, | ||||||
|  |         #[serde(default)] | ||||||
|  |         forced_state: SetRelayForcedStateReq, | ||||||
|         should_be_on: bool, |         should_be_on: bool, | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -439,6 +525,7 @@ mod test { | |||||||
|                 let s = engine.relay_state(r.id); |                 let s = engine.relay_state(r.id); | ||||||
|                 s.on = r.on; |                 s.on = r.on; | ||||||
|                 s.since = time_secs() as usize - r.r#for; |                 s.since = time_secs() as usize - r.r#for; | ||||||
|  |                 s.forced_state = r.forced_state.to_forced_state() | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ use crate::server::custom_error::HttpResult; | |||||||
| use crate::server::devices_api::jwt_parser::JWTRequest; | use crate::server::devices_api::jwt_parser::JWTRequest; | ||||||
| use actix_web::{HttpResponse, web}; | use actix_web::{HttpResponse, web}; | ||||||
|  |  | ||||||
| #[derive(Debug, serde::Deserialize)] | #[derive(Debug, serde::Deserialize, Clone)] | ||||||
| pub struct LogRequest { | pub struct LogRequest { | ||||||
|     severity: LogSeverity, |     severity: LogSeverity, | ||||||
|     message: String, |     message: String, | ||||||
|   | |||||||
| @@ -30,7 +30,7 @@ pub struct JWTRequest { | |||||||
| } | } | ||||||
|  |  | ||||||
| impl JWTRequest { | impl JWTRequest { | ||||||
|     pub async fn parse_jwt<E: DeserializeOwned>( |     pub async fn parse_jwt<E: DeserializeOwned + std::clone::Clone>( | ||||||
|         &self, |         &self, | ||||||
|         actor: WebEnergyActor, |         actor: WebEnergyActor, | ||||||
|     ) -> anyhow::Result<(Device, E)> { |     ) -> anyhow::Result<(Device, E)> { | ||||||
|   | |||||||
| @@ -130,7 +130,7 @@ pub async fn get_certificate(query: web::Query<ReqWithDevID>, actor: WebEnergyAc | |||||||
|         .body(cert)) |         .body(cert)) | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, serde::Serialize, serde::Deserialize)] | #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] | ||||||
| struct Claims { | struct Claims { | ||||||
|     info: DeviceInfo, |     info: DeviceInfo, | ||||||
| } | } | ||||||
| @@ -155,12 +155,11 @@ pub async fn sync_device(body: web::Json<JWTRequest>, actor: WebEnergyActor) -> | |||||||
|     let mut available_update = None; |     let mut available_update = None; | ||||||
|  |  | ||||||
|     // Check if the version is available |     // Check if the version is available | ||||||
|     if let Some(desired) = device.desired_version { |     if let Some(desired) = device.desired_version | ||||||
|         if claims.info.version < desired |         && claims.info.version < desired | ||||||
|             && ota_manager::update_exists(OTAPlatform::from_str(&claims.info.reference)?, &desired)? |         && ota_manager::update_exists(OTAPlatform::from_str(&claims.info.reference)?, &desired)? | ||||||
|         { |     { | ||||||
|             available_update = Some(desired); |         available_update = Some(desired); | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     Ok(HttpResponse::Ok().json(SyncResult { |     Ok(HttpResponse::Ok().json(SyncResult { | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ use crate::server::web_api::*; | |||||||
| use crate::server::web_app_controller; | use crate::server::web_app_controller; | ||||||
| use actix_cors::Cors; | use actix_cors::Cors; | ||||||
| use actix_identity::IdentityMiddleware; | use actix_identity::IdentityMiddleware; | ||||||
| use actix_identity::config::LogoutBehaviour; | use actix_identity::config::LogoutBehavior; | ||||||
| use actix_remote_ip::RemoteIPConfig; | use actix_remote_ip::RemoteIPConfig; | ||||||
| use actix_session::SessionMiddleware; | use actix_session::SessionMiddleware; | ||||||
| use actix_session::storage::CookieSessionStore; | use actix_session::storage::CookieSessionStore; | ||||||
| @@ -84,7 +84,7 @@ pub async fn secure_server(energy_actor: EnergyActorAddr) -> anyhow::Result<()> | |||||||
|         .build(); |         .build(); | ||||||
|  |  | ||||||
|         let identity_middleware = IdentityMiddleware::builder() |         let identity_middleware = IdentityMiddleware::builder() | ||||||
|             .logout_behaviour(LogoutBehaviour::PurgeSession) |             .logout_behavior(LogoutBehavior::PurgeSession) | ||||||
|             .visit_deadline(Some(Duration::from_secs( |             .visit_deadline(Some(Duration::from_secs( | ||||||
|                 constants::MAX_INACTIVITY_DURATION, |                 constants::MAX_INACTIVITY_DURATION, | ||||||
|             ))) |             ))) | ||||||
| @@ -231,6 +231,10 @@ pub async fn secure_server(energy_actor: EnergyActorAddr) -> anyhow::Result<()> | |||||||
|                 "/web_api/relay/{id}", |                 "/web_api/relay/{id}", | ||||||
|                 web::put().to(relays_controller::update), |                 web::put().to(relays_controller::update), | ||||||
|             ) |             ) | ||||||
|  |             .route( | ||||||
|  |                 "/web_api/relay/{id}/forced_state", | ||||||
|  |                 web::put().to(relays_controller::set_forced_state), | ||||||
|  |             ) | ||||||
|             .route( |             .route( | ||||||
|                 "/web_api/relay/{id}", |                 "/web_api/relay/{id}", | ||||||
|                 web::delete().to(relays_controller::delete), |                 web::delete().to(relays_controller::delete), | ||||||
|   | |||||||
| @@ -123,17 +123,17 @@ pub async fn set_desired_version( | |||||||
|  |  | ||||||
|     for d in devices { |     for d in devices { | ||||||
|         // Filter per platform |         // Filter per platform | ||||||
|         if let Some(p) = body.platform { |         if let Some(p) = body.platform | ||||||
|             if d.info.reference != p.to_string() { |             && d.info.reference != p.to_string() | ||||||
|                 continue; |         { | ||||||
|             } |             continue; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // Filter per device |         // Filter per device | ||||||
|         if let Some(ids) = &body.devices { |         if let Some(ids) = &body.devices | ||||||
|             if !ids.contains(&d.id) { |             && !ids.contains(&d.id) | ||||||
|                 continue; |         { | ||||||
|             } |             continue; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         actor |         actor | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| use crate::devices::device::{DeviceId, DeviceRelay, DeviceRelayID}; | use crate::devices::device::{DeviceId, DeviceRelay, DeviceRelayID}; | ||||||
| use crate::energy::energy_actor; | use crate::energy::energy_actor; | ||||||
|  | use crate::energy::engine::SetRelayForcedStateReq; | ||||||
| use crate::server::WebEnergyActor; | use crate::server::WebEnergyActor; | ||||||
| use crate::server::custom_error::HttpResult; | use crate::server::custom_error::HttpResult; | ||||||
| use actix_web::{HttpResponse, web}; | use actix_web::{HttpResponse, web}; | ||||||
| @@ -85,6 +86,29 @@ pub async fn update( | |||||||
|     Ok(HttpResponse::Accepted().finish()) |     Ok(HttpResponse::Accepted().finish()) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /// Set relay forced status | ||||||
|  | pub async fn set_forced_state( | ||||||
|  |     actor: WebEnergyActor, | ||||||
|  |     req: web::Json<SetRelayForcedStateReq>, | ||||||
|  |     path: web::Path<RelayIDInPath>, | ||||||
|  | ) -> HttpResult { | ||||||
|  |     // Check if relay exists first | ||||||
|  |     let list = actor.send(energy_actor::GetAllRelaysState).await?; | ||||||
|  |     if !list.into_iter().any(|r| r.id == path.id) { | ||||||
|  |         return Ok(HttpResponse::NotFound().json("Relay not found!")); | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     // Update relay forced state | ||||||
|  |     actor | ||||||
|  |         .send(energy_actor::SetRelayForcedState( | ||||||
|  |             path.id, | ||||||
|  |             req.to_forced_state(), | ||||||
|  |         )) | ||||||
|  |         .await??; | ||||||
|  |  | ||||||
|  |     Ok(HttpResponse::Accepted().finish()) | ||||||
|  | } | ||||||
|  |  | ||||||
| /// Delete an existing relay | /// Delete an existing relay | ||||||
| pub async fn delete(actor: WebEnergyActor, path: web::Path<RelayIDInPath>) -> HttpResult { | pub async fn delete(actor: WebEnergyActor, path: web::Path<RelayIDInPath>) -> HttpResult { | ||||||
|     actor |     actor | ||||||
|   | |||||||
							
								
								
									
										1718
									
								
								central_frontend/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1718
									
								
								central_frontend/package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -12,34 +12,34 @@ | |||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "@emotion/react": "^11.14.0", |     "@emotion/react": "^11.14.0", | ||||||
|     "@emotion/styled": "^11.14.1", |     "@emotion/styled": "^11.14.1", | ||||||
|     "@fontsource/roboto": "^5.2.6", |     "@fontsource/roboto": "^5.2.8", | ||||||
|     "@mdi/js": "^7.4.47", |     "@mdi/js": "^7.4.47", | ||||||
|     "@mdi/react": "^1.6.1", |     "@mdi/react": "^1.6.1", | ||||||
|     "@mui/icons-material": "^7.0.2", |     "@mui/icons-material": "^7.3.4", | ||||||
|     "@mui/material": "^7.0.2", |     "@mui/material": "^7.3.4", | ||||||
|     "@mui/x-charts": "^7.29.1", |     "@mui/x-charts": "^8.15.0", | ||||||
|     "@mui/x-date-pickers": "^7.29.4", |     "@mui/x-date-pickers": "^8.15.0", | ||||||
|     "date-and-time": "^3.6.0", |     "date-and-time": "^4.1.0", | ||||||
|     "dayjs": "^1.11.13", |     "dayjs": "^1.11.18", | ||||||
|     "filesize": "^10.1.6", |     "filesize": "^11.0.13", | ||||||
|     "react": "^19.1.0", |     "react": "^19.2.0", | ||||||
|     "react-dom": "^19.1.0", |     "react-dom": "^19.2.0", | ||||||
|     "react-router-dom": "^7.7.0", |     "react-router-dom": "^7.9.4", | ||||||
|     "semver": "^7.7.2" |     "semver": "^7.7.3" | ||||||
|   }, |   }, | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|     "@types/react": "^19.1.8", |     "@types/react": "^19.2.2", | ||||||
|     "@types/react-dom": "^19.1.6", |     "@types/react-dom": "^19.2.2", | ||||||
|     "@types/semver": "^7.7.0", |     "@types/semver": "^7.7.1", | ||||||
|     "@typescript-eslint/eslint-plugin": "^8.38.0", |     "@typescript-eslint/eslint-plugin": "^8.46.2", | ||||||
|     "@typescript-eslint/parser": "^8.37.0", |     "@typescript-eslint/parser": "^8.46.2", | ||||||
|     "@vitejs/plugin-react": "^4.7.0", |     "@vitejs/plugin-react": "^5.1.0", | ||||||
|     "eslint": "^9.31.0", |     "eslint": "^9.38.0", | ||||||
|     "eslint-plugin-react-hooks": "^5.2.0", |     "eslint-plugin-react-hooks": "^7.0.1", | ||||||
|     "eslint-plugin-react-refresh": "^0.4.20", |     "eslint-plugin-react-refresh": "^0.4.24", | ||||||
|     "globals": "^16.3.0", |     "globals": "^16.4.0", | ||||||
|     "typescript": "^5.8.3", |     "typescript": "^5.9.3", | ||||||
|     "typescript-eslint": "^8.24.1", |     "typescript-eslint": "^8.46.2", | ||||||
|     "vite": "^6.3.5" |     "vite": "^7.1.12" | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,10 +1,19 @@ | |||||||
| import { APIClient } from "./ApiClient"; | import { APIClient } from "./ApiClient"; | ||||||
| import { Device, DeviceRelay } from "./DeviceApi"; | import { Device, DeviceRelay } from "./DeviceApi"; | ||||||
|  |  | ||||||
|  | export type RelayForcedState = | ||||||
|  |   | { type: "None" } | ||||||
|  |   | { type: "Off" | "On"; until: number }; | ||||||
|  |  | ||||||
|  | export type SetRelayForcedState = | ||||||
|  |   | { type: "None" } | ||||||
|  |   | { type: "Off" | "On"; for_secs: number }; | ||||||
|  |  | ||||||
| export interface RelayStatus { | export interface RelayStatus { | ||||||
|   id: string; |   id: string; | ||||||
|   on: boolean; |   on: boolean; | ||||||
|   for: number; |   for: number; | ||||||
|  |   forced_state: RelayForcedState; | ||||||
| } | } | ||||||
|  |  | ||||||
| export type RelaysStatus = Map<string, RelayStatus>; | export type RelaysStatus = Map<string, RelayStatus>; | ||||||
| @@ -48,6 +57,20 @@ export class RelayApi { | |||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * Set relay forced state | ||||||
|  |    */ | ||||||
|  |   static async SetForcedState( | ||||||
|  |     relay: DeviceRelay, | ||||||
|  |     forced: SetRelayForcedState | ||||||
|  |   ): Promise<void> { | ||||||
|  |     await APIClient.exec({ | ||||||
|  |       method: "PUT", | ||||||
|  |       uri: `/relay/${relay.id}/forced_state`, | ||||||
|  |       jsonData: forced, | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   /** |   /** | ||||||
|    * Delete a relay configuration |    * Delete a relay configuration | ||||||
|    */ |    */ | ||||||
|   | |||||||
| @@ -84,7 +84,7 @@ export function DeployOTAUpdateDialogProvider(p: { | |||||||
|             </DialogContentText> |             </DialogContentText> | ||||||
|  |  | ||||||
|             <FormControl> |             <FormControl> | ||||||
|               <FormLabel>Gender</FormLabel> |               <FormLabel>Deployment target</FormLabel> | ||||||
|               <RadioGroup |               <RadioGroup | ||||||
|                 name="radio-buttons-group" |                 name="radio-buttons-group" | ||||||
|                 value={allDevices} |                 value={allDevices} | ||||||
|   | |||||||
| @@ -0,0 +1,53 @@ | |||||||
|  | import { | ||||||
|  |   Button, | ||||||
|  |   Dialog, | ||||||
|  |   DialogActions, | ||||||
|  |   DialogContent, | ||||||
|  |   DialogContentText, | ||||||
|  |   DialogTitle, | ||||||
|  |   TextField, | ||||||
|  | } from "@mui/material"; | ||||||
|  | import { DeviceRelay } from "../api/DeviceApi"; | ||||||
|  | import React from "react"; | ||||||
|  |  | ||||||
|  | export function SelectForcedStateDurationDialog(p: { | ||||||
|  |   relay: DeviceRelay; | ||||||
|  |   forcedState: string; | ||||||
|  |   onCancel: () => void; | ||||||
|  |   onSubmit: (duration: number) => void; | ||||||
|  | }): React.ReactElement { | ||||||
|  |   const [duration, setDuration] = React.useState(60); | ||||||
|  |  | ||||||
|  |   return ( | ||||||
|  |     <Dialog open onClose={p.onCancel}> | ||||||
|  |       <DialogTitle>Set forced relay state</DialogTitle> | ||||||
|  |       <DialogContent> | ||||||
|  |         <DialogContentText> | ||||||
|  |           Please specify the number of minutes the relay <i>{p.relay.name}</i>{" "} | ||||||
|  |           will remain in forced state <i>{p.forcedState}</i>: | ||||||
|  |         </DialogContentText> | ||||||
|  |  | ||||||
|  |         <TextField | ||||||
|  |           label="Duration (min)" | ||||||
|  |           variant="standard" | ||||||
|  |           value={Math.floor(duration / 60)} | ||||||
|  |           onChange={(e) => { | ||||||
|  |             const val = Number.parseInt(e.target.value); | ||||||
|  |             setDuration((Number.isNaN(val) ? 1 : val) * 60); | ||||||
|  |           }} | ||||||
|  |           fullWidth | ||||||
|  |           style={{ marginTop: "5px" }} | ||||||
|  |         /> | ||||||
|  |  | ||||||
|  |         <p>Equivalent in seconds: {duration} secs</p> | ||||||
|  |         <p>Equivalent in hours: {duration / 3600} hours</p> | ||||||
|  |       </DialogContent> | ||||||
|  |       <DialogActions> | ||||||
|  |         <Button onClick={p.onCancel}>Cancel</Button> | ||||||
|  |         <Button onClick={() => p.onSubmit(duration)} autoFocus> | ||||||
|  |           Start timer | ||||||
|  |         </Button> | ||||||
|  |       </DialogActions> | ||||||
|  |     </Dialog> | ||||||
|  |   ); | ||||||
|  | } | ||||||
| @@ -10,16 +10,16 @@ import { | |||||||
| } from "@mui/material"; | } from "@mui/material"; | ||||||
| import React from "react"; | import React from "react"; | ||||||
| import { Device, DeviceRelay } from "../../api/DeviceApi"; | import { Device, DeviceRelay } from "../../api/DeviceApi"; | ||||||
|  | import { RelayApi, RelayStatus } from "../../api/RelayApi"; | ||||||
| import { EditDeviceRelaysDialog } from "../../dialogs/EditDeviceRelaysDialog"; | import { EditDeviceRelaysDialog } from "../../dialogs/EditDeviceRelaysDialog"; | ||||||
| import { DeviceRouteCard } from "./DeviceRouteCard"; | import { useAlert } from "../../hooks/context_providers/AlertDialogProvider"; | ||||||
| import { useConfirm } from "../../hooks/context_providers/ConfirmDialogProvider"; | import { useConfirm } from "../../hooks/context_providers/ConfirmDialogProvider"; | ||||||
| import { useLoadingMessage } from "../../hooks/context_providers/LoadingMessageProvider"; | import { useLoadingMessage } from "../../hooks/context_providers/LoadingMessageProvider"; | ||||||
| import { RelayApi, RelayStatus } from "../../api/RelayApi"; |  | ||||||
| import { useSnackbar } from "../../hooks/context_providers/SnackbarProvider"; | import { useSnackbar } from "../../hooks/context_providers/SnackbarProvider"; | ||||||
| import { useAlert } from "../../hooks/context_providers/AlertDialogProvider"; |  | ||||||
| import { AsyncWidget } from "../../widgets/AsyncWidget"; | import { AsyncWidget } from "../../widgets/AsyncWidget"; | ||||||
| import { TimeWidget } from "../../widgets/TimeWidget"; |  | ||||||
| import { BoolText } from "../../widgets/BoolText"; | import { BoolText } from "../../widgets/BoolText"; | ||||||
|  | import { TimeWidget } from "../../widgets/TimeWidget"; | ||||||
|  | import { DeviceRouteCard } from "./DeviceRouteCard"; | ||||||
|  |  | ||||||
| export function DeviceRelays(p: { | export function DeviceRelays(p: { | ||||||
|   device: Device; |   device: Device; | ||||||
| @@ -145,7 +145,8 @@ function RelayEntryStatus( | |||||||
|       errMsg="Failed to load relay status!" |       errMsg="Failed to load relay status!" | ||||||
|       build={() => ( |       build={() => ( | ||||||
|         <> |         <> | ||||||
|           <BoolText val={state!.on} positive="ON" negative="OFF" /> for{" "} |           <BoolText val={state!.on} positive="ON" negative="OFF" />{" "} | ||||||
|  |           {state?.forced_state.type !== "None" && <b>Forced</b>} for{" "} | ||||||
|           <TimeWidget diff time={state!.for} /> |           <TimeWidget diff time={state!.for} /> | ||||||
|         </> |         </> | ||||||
|       )} |       )} | ||||||
|   | |||||||
| @@ -16,12 +16,13 @@ import React from "react"; | |||||||
| import { useNavigate } from "react-router-dom"; | import { useNavigate } from "react-router-dom"; | ||||||
| import { Device, DeviceApi, DeviceRelay, DeviceURL } from "../api/DeviceApi"; | import { Device, DeviceApi, DeviceRelay, DeviceURL } from "../api/DeviceApi"; | ||||||
| import { RelayApi, RelaysStatus } from "../api/RelayApi"; | import { RelayApi, RelaysStatus } from "../api/RelayApi"; | ||||||
|  | import { ServerApi } from "../api/ServerApi"; | ||||||
| import { AsyncWidget } from "../widgets/AsyncWidget"; | import { AsyncWidget } from "../widgets/AsyncWidget"; | ||||||
| import { BoolText } from "../widgets/BoolText"; | import { BoolText } from "../widgets/BoolText"; | ||||||
|  | import { CopyToClipboard } from "../widgets/CopyToClipboard"; | ||||||
|  | import { RelayForcedState } from "../widgets/RelayForcedState"; | ||||||
| import { SolarEnergyRouteContainer } from "../widgets/SolarEnergyRouteContainer"; | import { SolarEnergyRouteContainer } from "../widgets/SolarEnergyRouteContainer"; | ||||||
| import { TimeWidget } from "../widgets/TimeWidget"; | import { TimeWidget } from "../widgets/TimeWidget"; | ||||||
| import { CopyToClipboard } from "../widgets/CopyToClipboard"; |  | ||||||
| import { ServerApi } from "../api/ServerApi"; |  | ||||||
|  |  | ||||||
| export function RelaysListRoute(p: { | export function RelaysListRoute(p: { | ||||||
|   homeWidget?: boolean; |   homeWidget?: boolean; | ||||||
| @@ -104,6 +105,7 @@ function RelaysList(p: { | |||||||
|             <TableCell>Priority</TableCell> |             <TableCell>Priority</TableCell> | ||||||
|             <TableCell>Consumption</TableCell> |             <TableCell>Consumption</TableCell> | ||||||
|             <TableCell>Status</TableCell> |             <TableCell>Status</TableCell> | ||||||
|  |             <TableCell>Forced state</TableCell> | ||||||
|             <TableCell></TableCell> |             <TableCell></TableCell> | ||||||
|           </TableRow> |           </TableRow> | ||||||
|         </TableHead> |         </TableHead> | ||||||
| @@ -129,6 +131,13 @@ function RelaysList(p: { | |||||||
|                 />{" "} |                 />{" "} | ||||||
|                 for <TimeWidget diff time={p.status.get(row.id)!.for} /> |                 for <TimeWidget diff time={p.status.get(row.id)!.for} /> | ||||||
|               </TableCell> |               </TableCell> | ||||||
|  |               <TableCell> | ||||||
|  |                 <RelayForcedState | ||||||
|  |                   relay={row} | ||||||
|  |                   state={p.status.get(row.id)!} | ||||||
|  |                   onUpdated={p.onReload} | ||||||
|  |                 /> | ||||||
|  |               </TableCell> | ||||||
|               <TableCell> |               <TableCell> | ||||||
|                 <Tooltip title="Copy legacy api status"> |                 <Tooltip title="Copy legacy api status"> | ||||||
|                   <CopyToClipboard |                   <CopyToClipboard | ||||||
|   | |||||||
							
								
								
									
										79
									
								
								central_frontend/src/widgets/RelayForcedState.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								central_frontend/src/widgets/RelayForcedState.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,79 @@ | |||||||
|  | import { MenuItem, Select, SelectChangeEvent } from "@mui/material"; | ||||||
|  | import { DeviceRelay } from "../api/DeviceApi"; | ||||||
|  | import { RelayApi, RelayStatus, SetRelayForcedState } from "../api/RelayApi"; | ||||||
|  | import { TimeWidget } from "./TimeWidget"; | ||||||
|  | import { useLoadingMessage } from "../hooks/context_providers/LoadingMessageProvider"; | ||||||
|  | import { useAlert } from "../hooks/context_providers/AlertDialogProvider"; | ||||||
|  | import { useSnackbar } from "../hooks/context_providers/SnackbarProvider"; | ||||||
|  | import React from "react"; | ||||||
|  | import { SelectForcedStateDurationDialog } from "../dialogs/SelectForcedStateDurationDialog"; | ||||||
|  |  | ||||||
|  | export function RelayForcedState(p: { | ||||||
|  |   relay: DeviceRelay; | ||||||
|  |   state: RelayStatus; | ||||||
|  |   onUpdated: () => void; | ||||||
|  | }): React.ReactElement { | ||||||
|  |   const loadingMessage = useLoadingMessage(); | ||||||
|  |   const alert = useAlert(); | ||||||
|  |   const snackbar = useSnackbar(); | ||||||
|  |  | ||||||
|  |   const [futureStateType, setFutureStateType] = React.useState< | ||||||
|  |     string | undefined | ||||||
|  |   >(); | ||||||
|  |  | ||||||
|  |   const handleChange = (event: SelectChangeEvent) => { | ||||||
|  |     if (event.target.value == "None") { | ||||||
|  |       submitChange({ type: "None" }); | ||||||
|  |     } else { | ||||||
|  |       setFutureStateType(event.target.value); | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   const submitChange = async (state: SetRelayForcedState) => { | ||||||
|  |     try { | ||||||
|  |       loadingMessage.show("Setting forced state..."); | ||||||
|  |       await RelayApi.SetForcedState(p.relay, state); | ||||||
|  |       p.onUpdated(); | ||||||
|  |       snackbar("Forced state successfully updated!"); | ||||||
|  |     } catch (e) { | ||||||
|  |       console.error(`Failed to set relay forced state! ${e}`); | ||||||
|  |       alert(`Failed to set loading state for relay! ${e}`); | ||||||
|  |     } finally { | ||||||
|  |       loadingMessage.hide(); | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   return ( | ||||||
|  |     <> | ||||||
|  |       <Select | ||||||
|  |         value={p.state.forced_state.type} | ||||||
|  |         onChange={handleChange} | ||||||
|  |         size="small" | ||||||
|  |         variant="standard" | ||||||
|  |       > | ||||||
|  |         <MenuItem value={"None"}>None</MenuItem> | ||||||
|  |         <MenuItem value={"Off"}>Off</MenuItem> | ||||||
|  |         <MenuItem value={"On"}>On</MenuItem> | ||||||
|  |       </Select> | ||||||
|  |       {p.state.forced_state.type !== "None" && ( | ||||||
|  |         <> | ||||||
|  |           <TimeWidget future time={p.state.forced_state.until} /> left | ||||||
|  |         </> | ||||||
|  |       )} | ||||||
|  |  | ||||||
|  |       {futureStateType !== undefined && ( | ||||||
|  |         <SelectForcedStateDurationDialog | ||||||
|  |           {...p} | ||||||
|  |           forcedState={futureStateType} | ||||||
|  |           onCancel={() => setFutureStateType(undefined)} | ||||||
|  |           onSubmit={(d) => | ||||||
|  |             submitChange({ | ||||||
|  |               type: futureStateType as any, | ||||||
|  |               for_secs: d, | ||||||
|  |             }) | ||||||
|  |           } | ||||||
|  |         /> | ||||||
|  |       )} | ||||||
|  |     </> | ||||||
|  |   ); | ||||||
|  | } | ||||||
| @@ -106,7 +106,7 @@ export default function StatCard({ | |||||||
|           <Box sx={{ width: "100%", height: 100 }}> |           <Box sx={{ width: "100%", height: 100 }}> | ||||||
|             {data && interval && ( |             {data && interval && ( | ||||||
|               <SparkLineChart |               <SparkLineChart | ||||||
|                 colors={[chartColor]} |                 color={chartColor} | ||||||
|                 data={data} |                 data={data} | ||||||
|                 area |                 area | ||||||
|                 showHighlight |                 showHighlight | ||||||
|   | |||||||
| @@ -1,11 +1,11 @@ | |||||||
| import { Tooltip } from "@mui/material"; | import { Tooltip } from "@mui/material"; | ||||||
| import date from "date-and-time"; | import { format } from "date-and-time"; | ||||||
| import { time } from "../utils/DateUtils"; | import { time } from "../utils/DateUtils"; | ||||||
|  |  | ||||||
| export function formatDate(time: number): string { | export function formatDate(time: number): string { | ||||||
|   const t = new Date(); |   const t = new Date(); | ||||||
|   t.setTime(1000 * time); |   t.setTime(1000 * time); | ||||||
|   return date.format(t, "DD/MM/YYYY HH:mm:ss"); |   return format(t, "DD/MM/YYYY HH:mm:ss"); | ||||||
| } | } | ||||||
|  |  | ||||||
| export function timeDiff(a: number, b: number): string { | export function timeDiff(a: number, b: number): string { | ||||||
| @@ -51,13 +51,14 @@ export function timeDiff(a: number, b: number): string { | |||||||
|   return `${diffYears} years`; |   return `${diffYears} years`; | ||||||
| } | } | ||||||
|  |  | ||||||
| export function timeDiffFromNow(t: number): string { | export function timeDiffFromNow(t: number, future?: boolean): string { | ||||||
|   return timeDiff(t, time()); |   return future ? timeDiff(time(), t) : timeDiff(t, time()); | ||||||
| } | } | ||||||
|  |  | ||||||
| export function TimeWidget(p: { | export function TimeWidget(p: { | ||||||
|   time?: number; |   time?: number; | ||||||
|   diff?: boolean; |   diff?: boolean; | ||||||
|  |   future?: boolean; | ||||||
| }): React.ReactElement { | }): React.ReactElement { | ||||||
|   if (!p.time) return <></>; |   if (!p.time) return <></>; | ||||||
|   return ( |   return ( | ||||||
| @@ -65,7 +66,9 @@ export function TimeWidget(p: { | |||||||
|       title={formatDate(p.diff ? new Date().getTime() / 1000 - p.time : p.time)} |       title={formatDate(p.diff ? new Date().getTime() / 1000 - p.time : p.time)} | ||||||
|       arrow |       arrow | ||||||
|     > |     > | ||||||
|       <span>{p.diff ? timeDiff(0, p.time) : timeDiffFromNow(p.time)}</span> |       <span> | ||||||
|  |         {p.diff ? timeDiff(0, p.time) : timeDiffFromNow(p.time, p.future)} | ||||||
|  |       </span> | ||||||
|     </Tooltip> |     </Tooltip> | ||||||
|   ); |   ); | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										387
									
								
								custom_consumption/Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										387
									
								
								custom_consumption/Cargo.lock
									
									
									
										generated
									
									
									
								
							| @@ -90,8 +90,8 @@ dependencies = [ | |||||||
|  "accesskit_consumer", |  "accesskit_consumer", | ||||||
|  "hashbrown", |  "hashbrown", | ||||||
|  "static_assertions", |  "static_assertions", | ||||||
|  "windows", |  "windows 0.61.3", | ||||||
|  "windows-core", |  "windows-core 0.61.2", | ||||||
| ] | ] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| @@ -152,7 +152,7 @@ dependencies = [ | |||||||
|  "log", |  "log", | ||||||
|  "ndk", |  "ndk", | ||||||
|  "ndk-context", |  "ndk-context", | ||||||
|  "ndk-sys", |  "ndk-sys 0.6.0+11769913", | ||||||
|  "num_enum", |  "num_enum", | ||||||
|  "thiserror 1.0.69", |  "thiserror 1.0.69", | ||||||
| ] | ] | ||||||
| @@ -163,6 +163,15 @@ version = "0.2.2" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" | checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "android_system_properties" | ||||||
|  | version = "0.1.5" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" | ||||||
|  | dependencies = [ | ||||||
|  |  "libc", | ||||||
|  | ] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "anstream" | name = "anstream" | ||||||
| version = "0.6.18" | version = "0.6.18" | ||||||
| @@ -249,6 +258,15 @@ version = "1.0.1" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" | checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "ash" | ||||||
|  | version = "0.38.0+1.3.281" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f" | ||||||
|  | dependencies = [ | ||||||
|  |  "libloading", | ||||||
|  | ] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "async-broadcast" | name = "async-broadcast" | ||||||
| version = "0.7.2" | version = "0.7.2" | ||||||
| @@ -473,6 +491,12 @@ dependencies = [ | |||||||
|  "serde", |  "serde", | ||||||
| ] | ] | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "block" | ||||||
|  | version = "0.1.6" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "block2" | name = "block2" | ||||||
| version = "0.5.1" | version = "0.5.1" | ||||||
| @@ -599,9 +623,9 @@ dependencies = [ | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "clap" | name = "clap" | ||||||
| version = "4.5.41" | version = "4.5.50" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "be92d32e80243a54711e5d7ce823c35c41c9d929dc4ab58e1276f625841aadf9" | checksum = "0c2cfd7bf8a6017ddaa4e32ffe7403d547790db06bd171c1c53926faab501623" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "clap_builder", |  "clap_builder", | ||||||
|  "clap_derive", |  "clap_derive", | ||||||
| @@ -609,9 +633,9 @@ dependencies = [ | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "clap_builder" | name = "clap_builder" | ||||||
| version = "4.5.41" | version = "4.5.50" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "707eab41e9622f9139419d573eca0900137718000c517d47da73045f54331c3d" | checksum = "0a4c05b9e80c5ccd3a7ef080ad7b6ba7d6fc00a985b8b157197075677c82c7a0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "anstream", |  "anstream", | ||||||
|  "anstyle", |  "anstyle", | ||||||
| @@ -621,9 +645,9 @@ dependencies = [ | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "clap_derive" | name = "clap_derive" | ||||||
| version = "4.5.41" | version = "4.5.49" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491" | checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "heck", |  "heck", | ||||||
|  "proc-macro2", |  "proc-macro2", | ||||||
| @@ -820,9 +844,9 @@ checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "ecolor" | name = "ecolor" | ||||||
| version = "0.32.0" | version = "0.32.3" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "4a631732d995184114016fab22fc7e3faf73d6841c2d7650395fe251fbcd9285" | checksum = "94bdf37f8d5bd9aa7f753573fdda9cf7343afa73dd28d7bfe9593bd9798fc07e" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "bytemuck", |  "bytemuck", | ||||||
|  "emath", |  "emath", | ||||||
| @@ -830,9 +854,9 @@ dependencies = [ | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "eframe" | name = "eframe" | ||||||
| version = "0.32.0" | version = "0.32.3" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "0c790ccfbb3dd556588342463454b2b2b13909e5fdce5bc2a1432a8aa69c8b7a" | checksum = "14d1c15e7bd136b309bd3487e6ffe5f668b354cd9768636a836dd738ac90eb0b" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "ahash", |  "ahash", | ||||||
|  "bytemuck", |  "bytemuck", | ||||||
| @@ -866,9 +890,9 @@ dependencies = [ | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "egui" | name = "egui" | ||||||
| version = "0.32.0" | version = "0.32.3" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "8470210c95a42cc985d9ffebfd5067eea55bdb1c3f7611484907db9639675e28" | checksum = "5d5d0306cd61ca75e29682926d71f2390160247f135965242e904a636f51c0dc" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "accesskit", |  "accesskit", | ||||||
|  "ahash", |  "ahash", | ||||||
| @@ -884,9 +908,9 @@ dependencies = [ | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "egui-wgpu" | name = "egui-wgpu" | ||||||
| version = "0.32.0" | version = "0.32.3" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "14de9942d8b9e99e2d830403c208ab1a6e052e925a7456a4f6f66d567d90de1d" | checksum = "c12eca13293f8eba27a32aaaa1c765bfbf31acd43e8d30d5881dcbe5e99ca0c7" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "ahash", |  "ahash", | ||||||
|  "bytemuck", |  "bytemuck", | ||||||
| @@ -904,9 +928,9 @@ dependencies = [ | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "egui-winit" | name = "egui-winit" | ||||||
| version = "0.32.0" | version = "0.32.3" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "c490804a035cec9c826082894a3e1ecf4198accd3817deb10f7919108ebafab0" | checksum = "f95d0a91f9cb0dc2e732d49c2d521ac8948e1f0b758f306fb7b14d6f5db3927f" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "accesskit_winit", |  "accesskit_winit", | ||||||
|  "ahash", |  "ahash", | ||||||
| @@ -924,9 +948,9 @@ dependencies = [ | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "egui_glow" | name = "egui_glow" | ||||||
| version = "0.32.0" | version = "0.32.3" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "d44f3fd4fdc5f960c9e9ef7327c26647edc3141abf96102980647129d49358e6" | checksum = "cc7037813341727937f9e22f78d912f3e29bc3c46e2f40a9e82bb51cbf5e4cfb" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "ahash", |  "ahash", | ||||||
|  "bytemuck", |  "bytemuck", | ||||||
| @@ -942,9 +966,9 @@ dependencies = [ | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "emath" | name = "emath" | ||||||
| version = "0.32.0" | version = "0.32.3" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "45f057b141e7e46340c321400be74b793543b1b213036f0f989c35d35957c32e" | checksum = "45fd7bc25f769a3c198fe1cf183124bf4de3bd62ef7b4f1eaf6b08711a3af8db" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "bytemuck", |  "bytemuck", | ||||||
| ] | ] | ||||||
| @@ -1001,9 +1025,9 @@ dependencies = [ | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "epaint" | name = "epaint" | ||||||
| version = "0.32.0" | version = "0.32.3" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "94cca02195f0552c17cabdc02f39aa9ab6fbd815dac60ab1cd3d5b0aa6f9551c" | checksum = "63adcea970b7a13094fe97a36ab9307c35a750f9e24bf00bb7ef3de573e0fddb" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "ab_glyph", |  "ab_glyph", | ||||||
|  "ahash", |  "ahash", | ||||||
| @@ -1019,9 +1043,9 @@ dependencies = [ | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "epaint_default_fonts" | name = "epaint_default_fonts" | ||||||
| version = "0.32.0" | version = "0.32.3" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "e8495e11ed527dff39663b8c36b6c2b2799d7e4287fb90556e455d72eca0b4d3" | checksum = "1537accc50c9cab5a272c39300bdd0dd5dca210f6e5e8d70be048df9596e7ca2" | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "equivalent" | name = "equivalent" | ||||||
| @@ -1311,6 +1335,57 @@ dependencies = [ | |||||||
|  "gl_generator", |  "gl_generator", | ||||||
| ] | ] | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "gpu-alloc" | ||||||
|  | version = "0.6.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" | ||||||
|  | dependencies = [ | ||||||
|  |  "bitflags 2.9.0", | ||||||
|  |  "gpu-alloc-types", | ||||||
|  | ] | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "gpu-alloc-types" | ||||||
|  | version = "0.3.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" | ||||||
|  | dependencies = [ | ||||||
|  |  "bitflags 2.9.0", | ||||||
|  | ] | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "gpu-allocator" | ||||||
|  | version = "0.27.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "c151a2a5ef800297b4e79efa4f4bec035c5f51d5ae587287c9b952bdf734cacd" | ||||||
|  | dependencies = [ | ||||||
|  |  "log", | ||||||
|  |  "presser", | ||||||
|  |  "thiserror 1.0.69", | ||||||
|  |  "windows 0.58.0", | ||||||
|  | ] | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "gpu-descriptor" | ||||||
|  | version = "0.3.2" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" | ||||||
|  | dependencies = [ | ||||||
|  |  "bitflags 2.9.0", | ||||||
|  |  "gpu-descriptor-types", | ||||||
|  |  "hashbrown", | ||||||
|  | ] | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "gpu-descriptor-types" | ||||||
|  | version = "0.2.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" | ||||||
|  | dependencies = [ | ||||||
|  |  "bitflags 2.9.0", | ||||||
|  | ] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "half" | name = "half" | ||||||
| version = "2.6.0" | version = "2.6.0" | ||||||
| @@ -1603,6 +1678,17 @@ dependencies = [ | |||||||
|  "wasm-bindgen", |  "wasm-bindgen", | ||||||
| ] | ] | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "khronos-egl" | ||||||
|  | version = "6.0.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" | ||||||
|  | dependencies = [ | ||||||
|  |  "libc", | ||||||
|  |  "libloading", | ||||||
|  |  "pkg-config", | ||||||
|  | ] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "khronos_api" | name = "khronos_api" | ||||||
| version = "3.1.0" | version = "3.1.0" | ||||||
| @@ -1684,9 +1770,18 @@ dependencies = [ | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "log" | name = "log" | ||||||
| version = "0.4.27" | version = "0.4.28" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" | checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "malloc_buf" | ||||||
|  | version = "0.0.6" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" | ||||||
|  | dependencies = [ | ||||||
|  |  "libc", | ||||||
|  | ] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "memchr" | name = "memchr" | ||||||
| @@ -1712,6 +1807,21 @@ dependencies = [ | |||||||
|  "autocfg", |  "autocfg", | ||||||
| ] | ] | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "metal" | ||||||
|  | version = "0.31.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "f569fb946490b5743ad69813cb19629130ce9374034abe31614a36402d18f99e" | ||||||
|  | dependencies = [ | ||||||
|  |  "bitflags 2.9.0", | ||||||
|  |  "block", | ||||||
|  |  "core-graphics-types", | ||||||
|  |  "foreign-types", | ||||||
|  |  "log", | ||||||
|  |  "objc", | ||||||
|  |  "paste", | ||||||
|  | ] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "miniz_oxide" | name = "miniz_oxide" | ||||||
| version = "0.8.5" | version = "0.8.5" | ||||||
| @@ -1741,6 +1851,7 @@ dependencies = [ | |||||||
|  "num-traits", |  "num-traits", | ||||||
|  "once_cell", |  "once_cell", | ||||||
|  "rustc-hash", |  "rustc-hash", | ||||||
|  |  "spirv", | ||||||
|  "strum", |  "strum", | ||||||
|  "thiserror 2.0.12", |  "thiserror 2.0.12", | ||||||
|  "unicode-ident", |  "unicode-ident", | ||||||
| @@ -1755,7 +1866,7 @@ dependencies = [ | |||||||
|  "bitflags 2.9.0", |  "bitflags 2.9.0", | ||||||
|  "jni-sys", |  "jni-sys", | ||||||
|  "log", |  "log", | ||||||
|  "ndk-sys", |  "ndk-sys 0.6.0+11769913", | ||||||
|  "num_enum", |  "num_enum", | ||||||
|  "raw-window-handle", |  "raw-window-handle", | ||||||
|  "thiserror 1.0.69", |  "thiserror 1.0.69", | ||||||
| @@ -1767,6 +1878,15 @@ version = "0.1.1" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" | checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "ndk-sys" | ||||||
|  | version = "0.5.0+25.2.9519653" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691" | ||||||
|  | dependencies = [ | ||||||
|  |  "jni-sys", | ||||||
|  | ] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "ndk-sys" | name = "ndk-sys" | ||||||
| version = "0.6.0+11769913" | version = "0.6.0+11769913" | ||||||
| @@ -1826,6 +1946,15 @@ dependencies = [ | |||||||
|  "syn", |  "syn", | ||||||
| ] | ] | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "objc" | ||||||
|  | version = "0.2.7" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" | ||||||
|  | dependencies = [ | ||||||
|  |  "malloc_buf", | ||||||
|  | ] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "objc-sys" | name = "objc-sys" | ||||||
| version = "0.3.5" | version = "0.3.5" | ||||||
| @@ -2063,6 +2192,15 @@ dependencies = [ | |||||||
|  "libredox", |  "libredox", | ||||||
| ] | ] | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "ordered-float" | ||||||
|  | version = "4.6.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951" | ||||||
|  | dependencies = [ | ||||||
|  |  "num-traits", | ||||||
|  | ] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "ordered-stream" | name = "ordered-stream" | ||||||
| version = "0.2.0" | version = "0.2.0" | ||||||
| @@ -2111,6 +2249,12 @@ dependencies = [ | |||||||
|  "windows-targets 0.52.6", |  "windows-targets 0.52.6", | ||||||
| ] | ] | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "paste" | ||||||
|  | version = "1.0.15" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "percent-encoding" | name = "percent-encoding" | ||||||
| version = "2.3.1" | version = "2.3.1" | ||||||
| @@ -2209,6 +2353,12 @@ dependencies = [ | |||||||
|  "portable-atomic", |  "portable-atomic", | ||||||
| ] | ] | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "presser" | ||||||
|  | version = "0.3.1" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "proc-macro-crate" | name = "proc-macro-crate" | ||||||
| version = "3.3.0" | version = "3.3.0" | ||||||
| @@ -2267,6 +2417,12 @@ version = "5.2.0" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" | checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "range-alloc" | ||||||
|  | version = "0.1.4" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "c3d6831663a5098ea164f89cff59c6284e95f4e3c76ce9848d4529f5ccca9bde" | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "raw-window-handle" | name = "raw-window-handle" | ||||||
| version = "0.6.2" | version = "0.6.2" | ||||||
| @@ -2519,6 +2675,15 @@ dependencies = [ | |||||||
|  "serde", |  "serde", | ||||||
| ] | ] | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "spirv" | ||||||
|  | version = "0.3.0+sdk-1.3.268.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" | ||||||
|  | dependencies = [ | ||||||
|  |  "bitflags 2.9.0", | ||||||
|  | ] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "stable_deref_trait" | name = "stable_deref_trait" | ||||||
| version = "1.2.0" | version = "1.2.0" | ||||||
| @@ -2920,13 +3085,13 @@ dependencies = [ | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "wayland-backend" | name = "wayland-backend" | ||||||
| version = "0.3.8" | version = "0.3.11" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "b7208998eaa3870dad37ec8836979581506e0c5c64c20c9e79e9d2a10d6f47bf" | checksum = "673a33c33048a5ade91a6b139580fa174e19fb0d23f396dca9fa15f2e1e49b35" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "cc", |  "cc", | ||||||
|  "downcast-rs", |  "downcast-rs", | ||||||
|  "rustix 0.38.44", |  "rustix 1.0.3", | ||||||
|  "scoped-tls", |  "scoped-tls", | ||||||
|  "smallvec", |  "smallvec", | ||||||
|  "wayland-sys", |  "wayland-sys", | ||||||
| @@ -2934,12 +3099,12 @@ dependencies = [ | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "wayland-client" | name = "wayland-client" | ||||||
| version = "0.31.8" | version = "0.31.11" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "c2120de3d33638aaef5b9f4472bff75f07c56379cf76ea320bd3a3d65ecaf73f" | checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "bitflags 2.9.0", |  "bitflags 2.9.0", | ||||||
|  "rustix 0.38.44", |  "rustix 1.0.3", | ||||||
|  "wayland-backend", |  "wayland-backend", | ||||||
|  "wayland-scanner", |  "wayland-scanner", | ||||||
| ] | ] | ||||||
| @@ -2968,9 +3133,9 @@ dependencies = [ | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "wayland-protocols" | name = "wayland-protocols" | ||||||
| version = "0.32.6" | version = "0.32.9" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "0781cf46869b37e36928f7b432273c0995aa8aed9552c556fb18754420541efc" | checksum = "efa790ed75fbfd71283bd2521a1cfdc022aabcc28bdcff00851f9e4ae88d9901" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "bitflags 2.9.0", |  "bitflags 2.9.0", | ||||||
|  "wayland-backend", |  "wayland-backend", | ||||||
| @@ -2980,9 +3145,9 @@ dependencies = [ | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "wayland-protocols-plasma" | name = "wayland-protocols-plasma" | ||||||
| version = "0.3.6" | version = "0.3.9" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "7ccaacc76703fefd6763022ac565b590fcade92202492381c95b2edfdf7d46b3" | checksum = "a07a14257c077ab3279987c4f8bb987851bf57081b93710381daea94f2c2c032" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "bitflags 2.9.0", |  "bitflags 2.9.0", | ||||||
|  "wayland-backend", |  "wayland-backend", | ||||||
| @@ -3006,9 +3171,9 @@ dependencies = [ | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "wayland-scanner" | name = "wayland-scanner" | ||||||
| version = "0.31.6" | version = "0.31.7" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "896fdafd5d28145fce7958917d69f2fd44469b1d4e861cb5961bcbeebc6d1484" | checksum = "54cb1e9dc49da91950bdfd8b848c49330536d9d1fb03d4bfec8cae50caa50ae3" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "proc-macro2", |  "proc-macro2", | ||||||
|  "quick-xml 0.37.3", |  "quick-xml 0.37.3", | ||||||
| @@ -3017,9 +3182,9 @@ dependencies = [ | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "wayland-sys" | name = "wayland-sys" | ||||||
| version = "0.31.6" | version = "0.31.7" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "dbcebb399c77d5aa9fa5db874806ee7b4eba4e73650948e8f93963f128896615" | checksum = "34949b42822155826b41db8e5d0c1be3a2bd296c747577a43a3e6daefc296142" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "dlib", |  "dlib", | ||||||
|  "log", |  "log", | ||||||
| @@ -3083,6 +3248,7 @@ dependencies = [ | |||||||
|  "hashbrown", |  "hashbrown", | ||||||
|  "js-sys", |  "js-sys", | ||||||
|  "log", |  "log", | ||||||
|  |  "naga", | ||||||
|  "parking_lot", |  "parking_lot", | ||||||
|  "portable-atomic", |  "portable-atomic", | ||||||
|  "profiling", |  "profiling", | ||||||
| @@ -3090,6 +3256,7 @@ dependencies = [ | |||||||
|  "smallvec", |  "smallvec", | ||||||
|  "static_assertions", |  "static_assertions", | ||||||
|  "wasm-bindgen", |  "wasm-bindgen", | ||||||
|  |  "wasm-bindgen-futures", | ||||||
|  "web-sys", |  "web-sys", | ||||||
|  "wgpu-core", |  "wgpu-core", | ||||||
|  "wgpu-hal", |  "wgpu-hal", | ||||||
| @@ -3120,11 +3287,31 @@ dependencies = [ | |||||||
|  "rustc-hash", |  "rustc-hash", | ||||||
|  "smallvec", |  "smallvec", | ||||||
|  "thiserror 2.0.12", |  "thiserror 2.0.12", | ||||||
|  |  "wgpu-core-deps-apple", | ||||||
|  |  "wgpu-core-deps-emscripten", | ||||||
|  "wgpu-core-deps-windows-linux-android", |  "wgpu-core-deps-windows-linux-android", | ||||||
|  "wgpu-hal", |  "wgpu-hal", | ||||||
|  "wgpu-types", |  "wgpu-types", | ||||||
| ] | ] | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "wgpu-core-deps-apple" | ||||||
|  | version = "25.0.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "cfd488b3239b6b7b185c3b045c39ca6bf8af34467a4c5de4e0b1a564135d093d" | ||||||
|  | dependencies = [ | ||||||
|  |  "wgpu-hal", | ||||||
|  | ] | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "wgpu-core-deps-emscripten" | ||||||
|  | version = "25.0.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "f09ad7aceb3818e52539acc679f049d3475775586f3f4e311c30165cf2c00445" | ||||||
|  | dependencies = [ | ||||||
|  |  "wgpu-hal", | ||||||
|  | ] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "wgpu-core-deps-windows-linux-android" | name = "wgpu-core-deps-windows-linux-android" | ||||||
| version = "25.0.0" | version = "25.0.0" | ||||||
| @@ -3140,17 +3327,45 @@ version = "25.0.2" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "f968767fe4d3d33747bbd1473ccd55bf0f6451f55d733b5597e67b5deab4ad17" | checksum = "f968767fe4d3d33747bbd1473ccd55bf0f6451f55d733b5597e67b5deab4ad17" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  |  "android_system_properties", | ||||||
|  |  "arrayvec", | ||||||
|  |  "ash", | ||||||
|  |  "bit-set", | ||||||
|  "bitflags 2.9.0", |  "bitflags 2.9.0", | ||||||
|  |  "block", | ||||||
|  |  "bytemuck", | ||||||
|  |  "cfg-if", | ||||||
|  "cfg_aliases", |  "cfg_aliases", | ||||||
|  |  "core-graphics-types", | ||||||
|  |  "glow", | ||||||
|  |  "glutin_wgl_sys", | ||||||
|  |  "gpu-alloc", | ||||||
|  |  "gpu-allocator", | ||||||
|  |  "gpu-descriptor", | ||||||
|  |  "hashbrown", | ||||||
|  |  "js-sys", | ||||||
|  |  "khronos-egl", | ||||||
|  |  "libc", | ||||||
|  "libloading", |  "libloading", | ||||||
|  "log", |  "log", | ||||||
|  |  "metal", | ||||||
|  "naga", |  "naga", | ||||||
|  |  "ndk-sys 0.5.0+25.2.9519653", | ||||||
|  |  "objc", | ||||||
|  |  "ordered-float", | ||||||
|  "parking_lot", |  "parking_lot", | ||||||
|  "portable-atomic", |  "portable-atomic", | ||||||
|  |  "profiling", | ||||||
|  |  "range-alloc", | ||||||
|  "raw-window-handle", |  "raw-window-handle", | ||||||
|  "renderdoc-sys", |  "renderdoc-sys", | ||||||
|  |  "smallvec", | ||||||
|  "thiserror 2.0.12", |  "thiserror 2.0.12", | ||||||
|  |  "wasm-bindgen", | ||||||
|  |  "web-sys", | ||||||
|  "wgpu-types", |  "wgpu-types", | ||||||
|  |  "windows 0.58.0", | ||||||
|  |  "windows-core 0.58.0", | ||||||
| ] | ] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| @@ -3198,6 +3413,16 @@ version = "0.4.0" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "windows" | ||||||
|  | version = "0.58.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" | ||||||
|  | dependencies = [ | ||||||
|  |  "windows-core 0.58.0", | ||||||
|  |  "windows-targets 0.52.6", | ||||||
|  | ] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "windows" | name = "windows" | ||||||
| version = "0.61.3" | version = "0.61.3" | ||||||
| @@ -3205,7 +3430,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||||||
| checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" | checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "windows-collections", |  "windows-collections", | ||||||
|  "windows-core", |  "windows-core 0.61.2", | ||||||
|  "windows-future", |  "windows-future", | ||||||
|  "windows-link", |  "windows-link", | ||||||
|  "windows-numerics", |  "windows-numerics", | ||||||
| @@ -3217,7 +3442,20 @@ version = "0.2.0" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" | checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "windows-core", |  "windows-core 0.61.2", | ||||||
|  | ] | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "windows-core" | ||||||
|  | version = "0.58.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" | ||||||
|  | dependencies = [ | ||||||
|  |  "windows-implement 0.58.0", | ||||||
|  |  "windows-interface 0.58.0", | ||||||
|  |  "windows-result 0.2.0", | ||||||
|  |  "windows-strings 0.1.0", | ||||||
|  |  "windows-targets 0.52.6", | ||||||
| ] | ] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| @@ -3226,11 +3464,11 @@ version = "0.61.2" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" | checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "windows-implement", |  "windows-implement 0.60.0", | ||||||
|  "windows-interface", |  "windows-interface 0.59.1", | ||||||
|  "windows-link", |  "windows-link", | ||||||
|  "windows-result", |  "windows-result 0.3.4", | ||||||
|  "windows-strings", |  "windows-strings 0.4.2", | ||||||
| ] | ] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| @@ -3239,11 +3477,22 @@ version = "0.2.1" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" | checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "windows-core", |  "windows-core 0.61.2", | ||||||
|  "windows-link", |  "windows-link", | ||||||
|  "windows-threading", |  "windows-threading", | ||||||
| ] | ] | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "windows-implement" | ||||||
|  | version = "0.58.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" | ||||||
|  | dependencies = [ | ||||||
|  |  "proc-macro2", | ||||||
|  |  "quote", | ||||||
|  |  "syn", | ||||||
|  | ] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "windows-implement" | name = "windows-implement" | ||||||
| version = "0.60.0" | version = "0.60.0" | ||||||
| @@ -3255,6 +3504,17 @@ dependencies = [ | |||||||
|  "syn", |  "syn", | ||||||
| ] | ] | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "windows-interface" | ||||||
|  | version = "0.58.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" | ||||||
|  | dependencies = [ | ||||||
|  |  "proc-macro2", | ||||||
|  |  "quote", | ||||||
|  |  "syn", | ||||||
|  | ] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "windows-interface" | name = "windows-interface" | ||||||
| version = "0.59.1" | version = "0.59.1" | ||||||
| @@ -3278,10 +3538,19 @@ version = "0.2.0" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" | checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "windows-core", |  "windows-core 0.61.2", | ||||||
|  "windows-link", |  "windows-link", | ||||||
| ] | ] | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "windows-result" | ||||||
|  | version = "0.2.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" | ||||||
|  | dependencies = [ | ||||||
|  |  "windows-targets 0.52.6", | ||||||
|  | ] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "windows-result" | name = "windows-result" | ||||||
| version = "0.3.4" | version = "0.3.4" | ||||||
| @@ -3291,6 +3560,16 @@ dependencies = [ | |||||||
|  "windows-link", |  "windows-link", | ||||||
| ] | ] | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "windows-strings" | ||||||
|  | version = "0.1.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" | ||||||
|  | dependencies = [ | ||||||
|  |  "windows-result 0.2.0", | ||||||
|  |  "windows-targets 0.52.6", | ||||||
|  | ] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "windows-strings" | name = "windows-strings" | ||||||
| version = "0.4.2" | version = "0.4.2" | ||||||
| @@ -3525,9 +3804,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "winit" | name = "winit" | ||||||
| version = "0.30.9" | version = "0.30.12" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "a809eacf18c8eca8b6635091543f02a5a06ddf3dad846398795460e6e0ae3cc0" | checksum = "c66d4b9ed69c4009f6321f762d6e61ad8a2389cd431b97cb1e146812e9e6c732" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "ahash", |  "ahash", | ||||||
|  "android-activity", |  "android-activity", | ||||||
|   | |||||||
| @@ -5,8 +5,8 @@ edition = "2024" | |||||||
|  |  | ||||||
| [dependencies] | [dependencies] | ||||||
| env_logger = "0.11.8" | env_logger = "0.11.8" | ||||||
| log = "0.4.27" | log = "0.4.28" | ||||||
| clap = { version = "4.5.41", features = ["derive", "env"] } | clap = { version = "4.5.50", features = ["derive", "env"] } | ||||||
| egui = "0.32.0" | egui = "0.32.3" | ||||||
| eframe = "0.32.0" | eframe = "0.32.3" | ||||||
| lazy_static = "1.5.0" | lazy_static = "1.5.0" | ||||||
|   | |||||||
| @@ -1,5 +1,3 @@ | |||||||
| #define CONFIG_ETH_USE_ESP32_EMAC |  | ||||||
|  |  | ||||||
| #include "esp_eth.h" | #include "esp_eth.h" | ||||||
| #include "esp_eth_mac.h" | #include "esp_eth_mac.h" | ||||||
| #include "esp_eth_com.h" | #include "esp_eth_com.h" | ||||||
|   | |||||||
| @@ -1 +1 @@ | |||||||
| 1.0.2 | 1.0.3 | ||||||
		Reference in New Issue
	
	Block a user