diff --git a/main/http_server/axe-os/src/app/app-routing.module.ts b/main/http_server/axe-os/src/app/app-routing.module.ts index f21107c0..141b1a49 100644 --- a/main/http_server/axe-os/src/app/app-routing.module.ts +++ b/main/http_server/axe-os/src/app/app-routing.module.ts @@ -1,8 +1,8 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { EditComponent } from './components/edit/edit.component'; import { HomeComponent } from './components/home/home.component'; +import { SettingsComponent } from './components/settings/settings.component'; import { SwarmComponent } from './components/swarm/swarm.component'; const routes: Routes = [ @@ -11,8 +11,8 @@ const routes: Routes = [ component: HomeComponent }, { - path: 'edit', - component: EditComponent + path: 'settings', + component: SettingsComponent }, { path: 'swarm', diff --git a/main/http_server/axe-os/src/app/app.component.html b/main/http_server/axe-os/src/app/app.component.html index 659f458f..cbd78fd2 100644 --- a/main/http_server/axe-os/src/app/app.component.html +++ b/main/http_server/axe-os/src/app/app.component.html @@ -9,7 +9,7 @@
Home
-
+
Settings
diff --git a/main/http_server/axe-os/src/app/app.component.scss b/main/http_server/axe-os/src/app/app.component.scss index 70a80fe5..f970db87 100644 --- a/main/http_server/axe-os/src/app/app.component.scss +++ b/main/http_server/axe-os/src/app/app.component.scss @@ -48,7 +48,7 @@ } .content { - padding-top: 26px; + padding-top: 50px; padding-left: 5vw; padding-right: 5vw; border-left: 1px solid #304562; diff --git a/main/http_server/axe-os/src/app/app.module.ts b/main/http_server/axe-os/src/app/app.module.ts index abd7b827..1587610b 100644 --- a/main/http_server/axe-os/src/app/app.module.ts +++ b/main/http_server/axe-os/src/app/app.module.ts @@ -12,24 +12,28 @@ import { EditComponent } from './components/edit/edit.component'; import { HeaderComponent } from './components/header/header.component'; import { HomeComponent } from './components/home/home.component'; import { LoadingComponent } from './components/loading/loading.component'; +import { SettingsComponent } from './components/settings/settings.component'; +import { SwarmComponent } from './components/swarm/swarm.component'; import { ANSIPipe } from './pipes/ansi.pipe'; import { DateAgoPipe } from './pipes/date-ago.pipe'; -import { SwarmComponent } from './components/swarm/swarm.component'; const components = [ AppComponent, - HeaderComponent + HeaderComponent, + EditComponent, + HomeComponent, + LoadingComponent, + SettingsComponent ]; @NgModule({ declarations: [ ...components, - EditComponent, - HomeComponent, - LoadingComponent, + ANSIPipe, DateAgoPipe, - SwarmComponent + SwarmComponent, + SettingsComponent ], imports: [ BrowserModule, diff --git a/main/http_server/axe-os/src/app/components/edit/edit.component.html b/main/http_server/axe-os/src/app/components/edit/edit.component.html index 9e15a1f5..8ffbc26b 100644 --- a/main/http_server/axe-os/src/app/components/edit/edit.component.html +++ b/main/http_server/axe-os/src/app/components/edit/edit.component.html @@ -1,148 +1,128 @@ -
-

Settings

- -
-
- - -
- -
- - -
-
- - -
-
- - -
-
- - -
-
- - -
- - - -
- - -
- -
- - -
-
- - - -
- - -
- -
- - -
-
- - -
- - -
- -
- - -
-
+ + +
+ + +
+ +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
- - + +
- - + + +
+
+ + + +
+ +
- -
- - +
+ +
-
- - You must restart this device after saving for changes to take effect. + + + +
+ +
- - -
+
+ + +
+ +
+ + +
-
+
+ + +
-
-

Update Firmware {{firmwareUpdateProgress}}%

- -
- (esp-miner.bin) -
-
-

Update Website {{websiteUpdateProgress}}%

- -
- (www.bin) -
\ No newline at end of file +
+ + +
+
+ + You must restart this device after saving for changes to take effect. +
+ + + + \ No newline at end of file diff --git a/main/http_server/axe-os/src/app/components/edit/edit.component.ts b/main/http_server/axe-os/src/app/components/edit/edit.component.ts index 6610cc98..8b04f175 100644 --- a/main/http_server/axe-os/src/app/components/edit/edit.component.ts +++ b/main/http_server/axe-os/src/app/components/edit/edit.component.ts @@ -1,4 +1,4 @@ -import { HttpErrorResponse, HttpEventType } from '@angular/common/http'; +import { HttpErrorResponse } from '@angular/common/http'; import { Component } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { ToastrService } from 'ngx-toastr'; @@ -111,81 +111,4 @@ export class EditComponent { }); } - otaUpdate(event: any) { - const file = event.target?.files.item(0) as File; - - if (file.name != 'esp-miner.bin') { - this.toastrService.error('Incorrect file, looking for esp-miner.bin.', 'Error'); - return; - } - - this.systemService.performOTAUpdate(file) - .pipe(this.loadingService.lockUIUntilComplete()) - .subscribe({ - next: (event) => { - if (event.type === HttpEventType.UploadProgress) { - this.firmwareUpdateProgress = Math.round((event.loaded / (event.total as number)) * 100); - } else if (event.type === HttpEventType.Response) { - if (event.ok) { - this.toastrService.success('Firmware updated', 'Success!'); - - } else { - this.toastrService.error(event.statusText, 'Error'); - } - } - }, - error: (err) => { - this.toastrService.error(event.statusText, 'Error'); - }, - complete: () => { - this.firmwareUpdateProgress = null; - } - }); - } - otaWWWUpdate(event: any) { - const file = event.target?.files.item(0) as File; - if (file.name != 'www.bin') { - this.toastrService.error('Incorrect file, looking for www.bin.', 'Error'); - return; - } - - this.systemService.performWWWOTAUpdate(file) - .pipe( - this.loadingService.lockUIUntilComplete(), - ).subscribe({ - next: (event) => { - if (event.type === HttpEventType.UploadProgress) { - this.websiteUpdateProgress = Math.round((event.loaded / (event.total as number)) * 100); - } else if (event.type === HttpEventType.Response) { - if (event.ok) { - setTimeout(() => { - this.toastrService.success('Website updated', 'Success!'); - window.location.reload(); - }, 1000); - - } else { - this.toastrService.error(event.statusText, 'Error'); - } - } - }, - error: (err) => { - this.toastrService.error(event.statusText, 'Error'); - }, - complete: () => { - this.websiteUpdateProgress = null; - } - }); - } - - public restart() { - this.systemService.restart().subscribe(res => { - - }); - this.toastr.success('Success!', 'Bitaxe restarted'); - } - - - - - } diff --git a/main/http_server/axe-os/src/app/components/settings/settings.component.html b/main/http_server/axe-os/src/app/components/settings/settings.component.html new file mode 100644 index 00000000..8311164b --- /dev/null +++ b/main/http_server/axe-os/src/app/components/settings/settings.component.html @@ -0,0 +1,21 @@ +
+

Settings

+ + + + +
+ +
+

Update Firmware {{firmwareUpdateProgress}}%

+ +
+ (esp-miner.bin) +
+ +
+

Update Website {{websiteUpdateProgress}}%

+ +
+ (www.bin) +
\ No newline at end of file diff --git a/main/http_server/axe-os/src/app/components/settings/settings.component.scss b/main/http_server/axe-os/src/app/components/settings/settings.component.scss new file mode 100644 index 00000000..aeb89d19 --- /dev/null +++ b/main/http_server/axe-os/src/app/components/settings/settings.component.scss @@ -0,0 +1,31 @@ +input[type="text"], +input[type="number"], +input[type="password"], +input[type="range"] { + min-width: 250px; + max-width: 90%; +} + +select { + min-width: 268px; + max-width: 90%; +} + +.restart { + margin-bottom: 1.5rem; + +} + +@media only screen and (min-width:900px) { + + input[type="text"], + input[type="password"], + input[type="number"], + input[type="range"] { + min-width: 500px + } + + select { + min-width: 518px + } +} \ No newline at end of file diff --git a/main/http_server/axe-os/src/app/components/settings/settings.component.spec.ts b/main/http_server/axe-os/src/app/components/settings/settings.component.spec.ts new file mode 100644 index 00000000..c2333ad2 --- /dev/null +++ b/main/http_server/axe-os/src/app/components/settings/settings.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SettingsComponent } from './settings.component'; + +describe('SettingsComponent', () => { + let component: SettingsComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [SettingsComponent] + }); + fixture = TestBed.createComponent(SettingsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/main/http_server/axe-os/src/app/components/settings/settings.component.ts b/main/http_server/axe-os/src/app/components/settings/settings.component.ts new file mode 100644 index 00000000..04b71d8e --- /dev/null +++ b/main/http_server/axe-os/src/app/components/settings/settings.component.ts @@ -0,0 +1,191 @@ +import { HttpErrorResponse, HttpEventType } from '@angular/common/http'; +import { Component } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { ToastrService } from 'ngx-toastr'; +import { startWith } from 'rxjs'; +import { LoadingService } from 'src/app/services/loading.service'; +import { SystemService } from 'src/app/services/system.service'; +import { eASICModel } from 'src/models/enum/eASICModel'; + +@Component({ + selector: 'app-settings', + templateUrl: './settings.component.html', + styleUrls: ['./settings.component.scss'] +}) +export class SettingsComponent { + + public form!: FormGroup; + + public firmwareUpdateProgress: number | null = null; + public websiteUpdateProgress: number | null = null; + + + public devToolsOpen: boolean = false; + public eASICModel = eASICModel; + public ASICModel!: eASICModel; + + constructor( + private fb: FormBuilder, + private systemService: SystemService, + private toastr: ToastrService, + private toastrService: ToastrService, + private loadingService: LoadingService + ) { + + window.addEventListener('resize', this.checkDevTools); + this.checkDevTools(); + + this.systemService.getInfo() + .pipe(this.loadingService.lockUIUntilComplete()) + .subscribe(info => { + this.ASICModel = info.ASICModel; + this.form = this.fb.group({ + flipscreen: [info.flipscreen == 1], + invertscreen: [info.invertscreen == 1], + stratumURL: [info.stratumURL, [ + Validators.required, + Validators.pattern(/^(?!.*stratum\+tcp:\/\/).*$/), + Validators.pattern(/^[^:]*$/), + ]], + stratumPort: [info.stratumPort, [ + Validators.required, + Validators.pattern(/^[^:]*$/), + Validators.min(0), + Validators.max(65353) + ]], + stratumUser: [info.stratumUser, [Validators.required]], + ssid: [info.ssid, [Validators.required]], + wifiPass: [info.wifiPass, [Validators.required]], + coreVoltage: [info.coreVoltage, [Validators.required]], + frequency: [info.frequency, [Validators.required]], + autofanspeed: [info.autofanspeed == 1, [Validators.required]], + invertfanpolarity: [info.invertfanpolarity == 1, [Validators.required]], + fanspeed: [info.fanspeed, [Validators.required]], + }); + + this.form.controls['autofanspeed'].valueChanges.pipe( + startWith(this.form.controls['autofanspeed'].value) + ).subscribe(autofanspeed => { + if (autofanspeed) { + this.form.controls['fanspeed'].disable(); + } else { + this.form.controls['fanspeed'].enable(); + } + }); + }); + + } + private checkDevTools = () => { + if ( + window.outerWidth - window.innerWidth > 160 || + window.outerHeight - window.innerHeight > 160 + ) { + this.devToolsOpen = true; + } else { + this.devToolsOpen = false; + } + }; + + public updateSystem() { + + const form = this.form.getRawValue(); + + form.frequency = parseInt(form.frequency); + form.coreVoltage = parseInt(form.coreVoltage); + + // bools to ints + form.flipscreen = form.flipscreen == true ? 1 : 0; + form.invertscreen = form.invertscreen == true ? 1 : 0; + form.invertfanpolarity = form.invertfanpolarity == true ? 1 : 0; + form.autofanspeed = form.autofanspeed == true ? 1 : 0; + + this.systemService.updateSystem(form) + .pipe(this.loadingService.lockUIUntilComplete()) + .subscribe({ + next: () => { + this.toastr.success('Success!', 'Saved.'); + }, + error: (err: HttpErrorResponse) => { + this.toastr.error('Error.', `Could not save. ${err.message}`); + } + }); + } + + otaUpdate(event: any) { + const file = event.target?.files.item(0) as File; + + if (file.name != 'esp-miner.bin') { + this.toastrService.error('Incorrect file, looking for esp-miner.bin.', 'Error'); + return; + } + + this.systemService.performOTAUpdate(file) + .pipe(this.loadingService.lockUIUntilComplete()) + .subscribe({ + next: (event) => { + if (event.type === HttpEventType.UploadProgress) { + this.firmwareUpdateProgress = Math.round((event.loaded / (event.total as number)) * 100); + } else if (event.type === HttpEventType.Response) { + if (event.ok) { + this.toastrService.success('Firmware updated', 'Success!'); + + } else { + this.toastrService.error(event.statusText, 'Error'); + } + } + }, + error: (err) => { + this.toastrService.error(event.statusText, 'Error'); + }, + complete: () => { + this.firmwareUpdateProgress = null; + } + }); + } + otaWWWUpdate(event: any) { + const file = event.target?.files.item(0) as File; + if (file.name != 'www.bin') { + this.toastrService.error('Incorrect file, looking for www.bin.', 'Error'); + return; + } + + this.systemService.performWWWOTAUpdate(file) + .pipe( + this.loadingService.lockUIUntilComplete(), + ).subscribe({ + next: (event) => { + if (event.type === HttpEventType.UploadProgress) { + this.websiteUpdateProgress = Math.round((event.loaded / (event.total as number)) * 100); + } else if (event.type === HttpEventType.Response) { + if (event.ok) { + setTimeout(() => { + this.toastrService.success('Website updated', 'Success!'); + window.location.reload(); + }, 1000); + + } else { + this.toastrService.error(event.statusText, 'Error'); + } + } + }, + error: (err) => { + this.toastrService.error(event.statusText, 'Error'); + }, + complete: () => { + this.websiteUpdateProgress = null; + } + }); + } + + public restart() { + this.systemService.restart().subscribe(res => { + + }); + this.toastr.success('Success!', 'Bitaxe restarted'); + } + + + + + +} diff --git a/main/http_server/axe-os/src/app/components/swarm/swarm.component.html b/main/http_server/axe-os/src/app/components/swarm/swarm.component.html index 9242baab..cf003e35 100644 --- a/main/http_server/axe-os/src/app/components/swarm/swarm.component.html +++ b/main/http_server/axe-os/src/app/components/swarm/swarm.component.html @@ -3,6 +3,9 @@ +
+ +
@@ -13,18 +16,22 @@ + + - + - + + +
Power Temp Best DifficultyEditRestart Remove
{{axe.ip}}{{axe.ip}} {{axe.hashRate | number: '1.2-2'}} Gh/s {{axe.uptimeSeconds | dateAgo}} {{axe.sharesAccepted}} {{axe.power | number: '1.2-2'}} W {{axe.temp}} C {{axe.bestDiff}}
diff --git a/main/http_server/axe-os/src/app/components/swarm/swarm.component.scss b/main/http_server/axe-os/src/app/components/swarm/swarm.component.scss index 2f9eb2bf..ab66c8d5 100644 --- a/main/http_server/axe-os/src/app/components/swarm/swarm.component.scss +++ b/main/http_server/axe-os/src/app/components/swarm/swarm.component.scss @@ -19,4 +19,8 @@ th, td { padding: 1rem 1rem; border-bottom: 1px solid #304562; +} + +a { + color: white; } \ No newline at end of file diff --git a/main/http_server/axe-os/src/app/components/swarm/swarm.component.ts b/main/http_server/axe-os/src/app/components/swarm/swarm.component.ts index 882d6498..e1427f71 100644 --- a/main/http_server/axe-os/src/app/components/swarm/swarm.component.ts +++ b/main/http_server/axe-os/src/app/components/swarm/swarm.component.ts @@ -1,7 +1,7 @@ import { Component } from '@angular/core'; import { FormBuilder, FormGroup } from '@angular/forms'; import { ToastrService } from 'ngx-toastr'; -import { catchError, map, Observable, of, startWith, switchMap } from 'rxjs'; +import { BehaviorSubject, catchError, map, Observable, of, startWith, switchMap } from 'rxjs'; import { SystemService } from 'src/app/services/system.service'; @Component({ @@ -15,6 +15,8 @@ export class SwarmComponent { public swarm$: Observable[]>; + public refresh$: BehaviorSubject = new BehaviorSubject(null); + constructor( private fb: FormBuilder, private systemService: SystemService, @@ -28,7 +30,11 @@ export class SwarmComponent { map(swarmInfo => { return swarmInfo.map(({ ip }) => { // Make individual API calls for each IP - return this.systemService.getInfo(`http://${ip}`).pipe( + return this.refresh$.pipe( + switchMap(() => { + return this.systemService.getInfo(`http://${ip}`); + }) + ).pipe( startWith({ ip }), map(info => { return { @@ -70,6 +76,20 @@ export class SwarmComponent { } + public refresh() { + this.refresh$.next(null); + } + + public edit(axe: any) { + + } + public restart(axe: any) { + this.systemService.restart(`http://${axe.ip}`).subscribe(res => { + + }); + this.toastr.success('Success!', 'Bitaxe restarted'); + } + public remove(axe: any) { this.systemService.getSwarmInfo().pipe( switchMap((swarm: any) => { diff --git a/main/http_server/axe-os/src/app/services/system.service.ts b/main/http_server/axe-os/src/app/services/system.service.ts index d95f09e3..2cd07bc5 100644 --- a/main/http_server/axe-os/src/app/services/system.service.ts +++ b/main/http_server/axe-os/src/app/services/system.service.ts @@ -15,9 +15,9 @@ export class SystemService { private httpClient: HttpClient ) { } - public getInfo(ip: string = ''): Observable { + public getInfo(uri: string = ''): Observable { if (environment.production) { - return this.httpClient.get(`${ip}/api/system/info`) as Observable; + return this.httpClient.get(`${uri}/api/system/info`) as Observable; } else { return of( { @@ -53,8 +53,8 @@ export class SystemService { } } - public restart() { - return this.httpClient.post(`/api/system/restart`, {}); + public restart(uri: string = '') { + return this.httpClient.post(`${uri}/api/system/restart`, {}); } public updateSystem(update: any) {