Compare commits
	
		
			161 Commits
		
	
	
		
			901dd43b08
			...
			renovate/m
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| b5f2188664 | |||
| 66923ab226 | |||
| 3f3b1282bd | |||
| 95a1a446db | |||
| f5fe5b5339 | |||
| c844490c4e | |||
| fe951f4004 | |||
| 8c92b8bf27 | |||
| 8d715b407e | |||
| f11d25b2f0 | |||
| 35e64e977b | |||
| 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 | 
							
								
								
									
										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: {}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										708
									
								
								central_backend/Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										708
									
								
								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.43", features = ["derive", "env"] }
 | 
					clap = { version = "4.5.51", 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.142"
 | 
					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.17.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.2"
 | 
				
			||||||
tokio = { version = "1.47.1", 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.7.2"
 | 
					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
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1707
									
								
								central_frontend/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1707
									
								
								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.16.0",
 | 
				
			||||||
    "@mui/x-date-pickers": "^7.29.4",
 | 
					    "@mui/x-date-pickers": "^8.16.0",
 | 
				
			||||||
    "date-and-time": "^3.6.0",
 | 
					    "date-and-time": "^4.1.0",
 | 
				
			||||||
    "dayjs": "^1.11.13",
 | 
					    "dayjs": "^1.11.19",
 | 
				
			||||||
    "filesize": "^10.1.6",
 | 
					    "filesize": "^11.0.13",
 | 
				
			||||||
    "react": "^19.1.1",
 | 
					    "react": "^19.2.0",
 | 
				
			||||||
    "react-dom": "^19.1.1",
 | 
					    "react-dom": "^19.2.0",
 | 
				
			||||||
    "react-router-dom": "^7.7.1",
 | 
					    "react-router-dom": "^7.9.5",
 | 
				
			||||||
    "semver": "^7.7.2"
 | 
					    "semver": "^7.7.3"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "devDependencies": {
 | 
					  "devDependencies": {
 | 
				
			||||||
    "@types/react": "^19.1.9",
 | 
					    "@types/react": "^19.2.2",
 | 
				
			||||||
    "@types/react-dom": "^19.1.7",
 | 
					    "@types/react-dom": "^19.2.2",
 | 
				
			||||||
    "@types/semver": "^7.7.0",
 | 
					    "@types/semver": "^7.7.1",
 | 
				
			||||||
    "@typescript-eslint/eslint-plugin": "^8.39.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.32.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.43"
 | 
					version = "4.5.51"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "50fd97c9dc2399518aa331917ac6f274280ec5eb34e555dd291899745c48ec6f"
 | 
					checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5"
 | 
				
			||||||
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.43"
 | 
					version = "4.5.51"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "c35b5830294e1fa0462034af85cc95225a4cb07092c088c55bda3147cfcd8f65"
 | 
					checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a"
 | 
				
			||||||
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.43", features = ["derive", "env"] }
 | 
					clap = { version = "4.5.51", 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