From 8e5e5f02719ea87cbe2f0e79a9f71ee1f7819fec Mon Sep 17 00:00:00 2001 From: UnchartedBull Date: Tue, 17 Nov 2020 17:03:14 +0100 Subject: [PATCH] remove octoprint script + rework setup + scrollable custom actions --- main.js | 1 - package-lock.json | 5 - package.json | 1 - sample.config.json | 102 ---- src/app/app.component.ts | 68 ++- src/app/app.module.ts | 23 +- src/app/app.routing.module.ts | 8 +- src/app/app.service.ts | 1 - src/app/config/config.default.ts | 107 +++++ src/app/config/config.model.ts | 94 ++++ src/app/config/config.schema.ts | 290 +++++++++++ src/app/config/config.service.ts | 452 ++---------------- .../invalid.component.html} | 0 .../invalid.component.scss} | 0 .../invalid.component.ts} | 8 +- .../config/no-config/no-config.component.html | 358 -------------- .../config/no-config/no-config.component.ts | 353 -------------- .../discover-octoprint.component.html | 47 ++ .../discover-octoprint.component.scss | 49 ++ .../discover-octoprint.component.ts | 75 +++ .../extruder-information.component.html | 91 ++++ .../extruder-information.component.scss | 7 + .../extruder-information.component.ts | 36 ++ .../octoprint-authentication.component.html | 20 + .../octoprint-authentication.component.scss | 6 + .../octoprint-authentication.component.ts | 94 ++++ .../personalization.component.html | 24 + .../personalization.component.scss | 5 + .../personalization.component.ts | 33 ++ .../setup/plugins/plugins.component.html | 40 ++ .../setup/plugins/plugins.component.scss | 0 .../config/setup/plugins/plugins.component.ts | 52 ++ src/app/config/setup/setup.component.html | 94 ++++ .../setup.component.scss} | 127 +---- src/app/config/setup/setup.component.ts | 124 +++++ .../setup/welcome/welcome.component.html | 21 + .../setup/welcome/welcome.component.scss | 10 + .../config/setup/welcome/welcome.component.ts | 10 + src/app/control/control.component.scss | 5 + src/app/control/control.component.ts | 5 +- src/app/files.service.ts | 8 +- src/app/job.service.ts | 4 +- .../notification/notification.component.ts | 19 +- src/app/octoprint-script.service.ts | 66 --- src/app/octoprint/auth.service.ts | 31 ++ .../model/connection.ts} | 10 +- .../filesAPI.ts => octoprint/model/file.ts} | 12 +- .../jobAPI.ts => octoprint/model/job.ts} | 2 +- .../model/layerProgress.ts} | 2 +- .../model/printerProfile.ts} | 9 +- .../model/printerStatus.ts} | 2 +- .../plugin-service/layer-progress.service.ts | 4 +- src/app/plugin-service/psu-control.service.ts | 2 +- src/app/printer.service.ts | 8 +- src/app/printerprofile.service.ts | 28 +- src/app/settings/settings.component.html | 2 +- src/app/settings/settings.component.ts | 6 +- src/app/standby/standby.component.ts | 4 +- src/app/update/update.component.ts | 33 +- src/index.html | 4 - 60 files changed, 1573 insertions(+), 1529 deletions(-) delete mode 100644 sample.config.json create mode 100644 src/app/config/config.default.ts create mode 100644 src/app/config/config.model.ts create mode 100644 src/app/config/config.schema.ts rename src/app/config/{invalid-config/invalid-config.component.html => invalid/invalid.component.html} (100%) rename src/app/config/{invalid-config/invalid-config.component.scss => invalid/invalid.component.scss} (100%) rename src/app/config/{invalid-config/invalid-config.component.ts => invalid/invalid.component.ts} (60%) delete mode 100644 src/app/config/no-config/no-config.component.html delete mode 100644 src/app/config/no-config/no-config.component.ts create mode 100644 src/app/config/setup/discover-octoprint/discover-octoprint.component.html create mode 100644 src/app/config/setup/discover-octoprint/discover-octoprint.component.scss create mode 100644 src/app/config/setup/discover-octoprint/discover-octoprint.component.ts create mode 100644 src/app/config/setup/extruder-information/extruder-information.component.html create mode 100644 src/app/config/setup/extruder-information/extruder-information.component.scss create mode 100644 src/app/config/setup/extruder-information/extruder-information.component.ts create mode 100644 src/app/config/setup/octoprint-authentication/octoprint-authentication.component.html create mode 100644 src/app/config/setup/octoprint-authentication/octoprint-authentication.component.scss create mode 100644 src/app/config/setup/octoprint-authentication/octoprint-authentication.component.ts create mode 100644 src/app/config/setup/personalization/personalization.component.html create mode 100644 src/app/config/setup/personalization/personalization.component.scss create mode 100644 src/app/config/setup/personalization/personalization.component.ts create mode 100644 src/app/config/setup/plugins/plugins.component.html create mode 100644 src/app/config/setup/plugins/plugins.component.scss create mode 100644 src/app/config/setup/plugins/plugins.component.ts create mode 100644 src/app/config/setup/setup.component.html rename src/app/config/{no-config/no-config.component.scss => setup/setup.component.scss} (67%) create mode 100644 src/app/config/setup/setup.component.ts create mode 100644 src/app/config/setup/welcome/welcome.component.html create mode 100644 src/app/config/setup/welcome/welcome.component.scss create mode 100644 src/app/config/setup/welcome/welcome.component.ts delete mode 100644 src/app/octoprint-script.service.ts create mode 100644 src/app/octoprint/auth.service.ts rename src/app/{octoprint-api/connectionAPI.ts => octoprint/model/connection.ts} (57%) rename src/app/{octoprint-api/filesAPI.ts => octoprint/model/file.ts} (81%) rename src/app/{octoprint-api/jobAPI.ts => octoprint/model/job.ts} (95%) rename src/app/{octoprint-api/layerProgressAPI.ts => octoprint/model/layerProgress.ts} (85%) rename src/app/{octoprint-api/printerProfileAPI.ts => octoprint/model/printerProfile.ts} (59%) rename src/app/{octoprint-api/printerStatusAPI.ts => octoprint/model/printerStatus.ts} (95%) diff --git a/main.js b/main.js index c63a8c2ee..108ae819c 100644 --- a/main.js +++ b/main.js @@ -43,7 +43,6 @@ function createWindow() { nodeIntegration: true, enableRemoteModule: true, worldSafeExecuteJavaScript: true, - // TODO: enable + contextBridge (probably electron-ngx before release 12) contextIsolation: false, }, icon: path.join(__dirname, 'dist', 'assets', 'icon', 'icon.png'), diff --git a/package-lock.json b/package-lock.json index cd0f357f7..e60623151 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8828,11 +8828,6 @@ "@hapi/topo": "^5.0.0" } }, - "jquery": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.5.1.tgz", - "integrity": "sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg==" - }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", diff --git a/package.json b/package.json index d09c268f3..355d12e81 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,6 @@ "compare-versions": "^3.6.0", "electron-store": "^6.0.1", "got": "^11.8.0", - "jquery": "^3.5.1", "lodash": "^4.17.20", "ngx-electron": "^2.2.0", "ngx-spinner": "^10.0.1", diff --git a/sample.config.json b/sample.config.json deleted file mode 100644 index 300330e35..000000000 --- a/sample.config.json +++ /dev/null @@ -1,102 +0,0 @@ -{ - "config": { - "octoprint": { - "accessToken": "", - "url": "http://localhost:5000/api/" - }, - "printer": { - "name": "", - "xySpeed": 100, - "zSpeed": 5, - "defaultTemperatureFanSpeed": { - "hotend": 200, - "heatbed": 60, - "fan": 100 - } - }, - "filament": { - "density": 0, - "thickness": 0, - "feedLength": 470, - "feedSpeed": 30, - "feedSpeedSlow": 3, - "purgeDistance": 30 - }, - "plugins": { - "displayLayerProgress": { - "enabled": true - }, - "enclosure": { - "enabled": true, - "ambientSensorID": null, - "filament1SensorID": null, - "filament2SensorID": null - }, - "filamentManager": { - "enabled": true - }, - "preheatButton": { - "enabled": true - }, - "printTimeGenius": { - "enabled": true - }, - "psuControl": { - "enabled": true, - "turnOnPSUWhenExitingSleep": false - } - }, - "octodash": { - "customActions": [{ - "icon": "home", - "command": "G28", - "color": "#dcdde1", - "confirm": false, - "exit": false - }, - { - "icon": "ruler-vertical", - "command": "G29", - "color": "#44bd32", - "confirm": false, - "exit": false - }, - { - "icon": "fire-alt", - "command": "M140 S50; M104 S185", - "color": "#e1b12c", - "confirm": false, - "exit": true - }, - { - "icon": "snowflake", - "command": "M140 S0; M104 S0", - "color": "#0097e6", - "confirm": false, - "exit": true - }, - { - "icon": "redo-alt", - "command": "[!RELOAD]", - "color": "#7f8fa6", - "confirm": true, - "exit": false - }, - { - "icon": "skull", - "command": "[!KILL]", - "color": "#e84118", - "confirm": true, - "exit": false - } - ], - "fileSorting": { - "attribute": "name", - "order": "asc" - }, - "pollingInterval": 2000, - "touchscreen": true, - "turnScreenOffWhileSleeping": false - } - } -} diff --git a/src/app/app.component.ts b/src/app/app.component.ts index f05204933..c459ef353 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectorRef, Component, NgZone, OnInit } from '@angular/core'; +import { Component, NgZone, OnInit } from '@angular/core'; import { Router } from '@angular/router'; import _ from 'lodash'; import { ElectronService } from 'ngx-electron'; @@ -6,16 +6,6 @@ import { ElectronService } from 'ngx-electron'; import { AppService } from './app.service'; import { ConfigService } from './config/config.service'; import { NotificationService } from './notification/notification.service'; -import { OctoprintScriptService } from './octoprint-script.service'; - -declare global { - interface Window { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - require: any; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - process: any; - } -} @Component({ selector: 'app-root', @@ -26,12 +16,10 @@ export class AppComponent implements OnInit { public constructor( private service: AppService, private configService: ConfigService, - private octoprintScriptService: OctoprintScriptService, private notificationService: NotificationService, private router: Router, private electronService: ElectronService, - private changeDetector: ChangeDetectorRef, - private ngZone: NgZone, + private zone: NgZone, ) {} public activated = false; @@ -79,9 +67,9 @@ export class AppComponent implements OnInit { private waitForOctoprint() { this.electronService.ipcRenderer.on('octoprintReady', (_, octoprintReady: boolean) => { - this.ngZone.run(() => { + this.zone.run(() => { if (octoprintReady) { - this.initializeOctoprintService(); + this.connectWebsocket(); this.status = 'initializing'; } else { this.notificationService @@ -92,12 +80,11 @@ export class AppComponent implements OnInit { .then(this.checkOctoprintPort.bind(this)); this.status = 'no connection'; } - this.changeDetector.detectChanges(); }); }); this.electronService.ipcRenderer.on('waitPortError', (_, error: Error) => { - this.ngZone.run(() => { + this.zone.run(() => { this.notificationService.setError('System Error - please restart', error.message); }); }); @@ -112,30 +99,29 @@ export class AppComponent implements OnInit { host: urlNoProtocol.split(':')[0], port: Number(urlNoProtocol.split(':')[1].split('/')[0]), }); - this.changeDetector.detectChanges(); } - private initializeOctoprintService() { - const showPrinterConnectedTimeout = setTimeout(() => { - this.showConnectionHint = true; - }, 30000); - this.octoprintScriptService - .initialize(this.configService.getURL(''), this.configService.getAccessKey()) - .then(() => { - this.octoprintScriptService.authenticate(this.configService.getAccessKey()); - if (this.configService.isTouchscreen()) { - this.router.navigate(['/main-screen']); - } else { - this.router.navigate(['/main-screen-no-touch']); - } - }) - .catch(() => { - console.log('REJECTED'); - this.notificationService.setError( - "Can't get OctoPrint script!", - 'Please restart your machine. If the error persists open a new issue on GitHub.', - ); - }) - .finally(() => clearTimeout(showPrinterConnectedTimeout)); + private connectWebsocket() { + if (this.configService.isTouchscreen()) { + this.router.navigate(['/main-screen']); + } else { + this.router.navigate(['/main-screen-no-touch']); + } + // const showPrinterConnectedTimeout = setTimeout(() => { + // this.showConnectionHint = true; + // }, 30000); + // this.octoprintScriptService + // .initialize(this.configService.getURL(''), this.configService.getAccessKey()) + // .then(() => { + // this.octoprintScriptService.authenticate(this.configService.getAccessKey()); + // }) + // .catch(() => { + // console.log('REJECTED'); + // this.notificationService.setError( + // "Can't get OctoPrint script!", + // 'Please restart your machine. If the error persists open a new issue on GitHub.', + // ); + // }) + // .finally(() => clearTimeout(showPrinterConnectedTimeout)); } } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 1a6f5d9c1..54cf02ef5 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -15,8 +15,14 @@ import { AppRoutingModule } from './app.routing.module'; import { AppService } from './app.service'; import { BottomBarComponent } from './bottom-bar/bottom-bar.component'; import { ConfigService } from './config/config.service'; -import { InvalidConfigComponent } from './config/invalid-config/invalid-config.component'; -import { NoConfigComponent } from './config/no-config/no-config.component'; +import { ConfigInvalidComponent } from './config/invalid/invalid.component'; +import { DiscoverOctoprintComponent } from './config/setup/discover-octoprint/discover-octoprint.component'; +import { ExtruderInformationComponent } from './config/setup/extruder-information/extruder-information.component'; +import { OctoprintAuthenticationComponent } from './config/setup/octoprint-authentication/octoprint-authentication.component'; +import { PersonalizationComponent } from './config/setup/personalization/personalization.component'; +import { PluginsComponent } from './config/setup/plugins/plugins.component'; +import { ConfigSetupComponent } from './config/setup/setup.component'; +import { WelcomeComponent } from './config/setup/welcome/welcome.component'; import { ControlComponent } from './control/control.component'; import { FilamentComponent } from './filament/filament.component'; import { FilesComponent } from './files/files.component'; @@ -29,7 +35,6 @@ import { MainScreenComponent } from './main-screen/main-screen.component'; import { MainScreenNoTouchComponent } from './main-screen/no-touch/main-screen-no-touch.component'; import { NotificationComponent } from './notification/notification.component'; import { NotificationService } from './notification/notification.service'; -import { OctoprintScriptService } from './octoprint-script.service'; import { PrintControlComponent } from './print-control/print-control.component'; import { PrinterStatusComponent } from './printer-status/printer-status.component'; import { PrinterService } from './printer.service'; @@ -45,14 +50,14 @@ import { URLSafePipe } from './url.pipe'; ControlComponent, FilamentComponent, FilesComponent, - InvalidConfigComponent, + ConfigInvalidComponent, JobStatusComponent, LayerProgressComponent, LongPress, MainMenuComponent, MainScreenComponent, MainScreenNoTouchComponent, - NoConfigComponent, + ConfigSetupComponent, NotificationComponent, PrintControlComponent, PrinterStatusComponent, @@ -60,6 +65,12 @@ import { URLSafePipe } from './url.pipe'; StandbyComponent, UpdateComponent, URLSafePipe, + WelcomeComponent, + DiscoverOctoprintComponent, + OctoprintAuthenticationComponent, + PersonalizationComponent, + ExtruderInformationComponent, + PluginsComponent, ], imports: [ BrowserModule, @@ -73,7 +84,7 @@ import { URLSafePipe } from './url.pipe'; BrowserAnimationsModule, MatRippleModule, ], - providers: [AppService, ConfigService, NotificationService, PrinterService, JobService, OctoprintScriptService], + providers: [AppService, ConfigService, NotificationService, PrinterService, JobService], bootstrap: [AppComponent], schemas: [CUSTOM_ELEMENTS_SCHEMA], }) diff --git a/src/app/app.routing.module.ts b/src/app/app.routing.module.ts index 9212a9a2a..012dff973 100644 --- a/src/app/app.routing.module.ts +++ b/src/app/app.routing.module.ts @@ -2,8 +2,8 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { InvalidConfigComponent } from './config/invalid-config/invalid-config.component'; -import { NoConfigComponent } from './config/no-config/no-config.component'; +import { ConfigInvalidComponent } from './config/invalid/invalid.component'; +import { ConfigSetupComponent } from './config/setup/setup.component'; import { ControlComponent } from './control/control.component'; import { FilamentComponent } from './filament/filament.component'; import { FilesComponent } from './files/files.component'; @@ -35,11 +35,11 @@ const routes: Routes = [ }, { path: 'invalid-config', - component: InvalidConfigComponent, + component: ConfigInvalidComponent, }, { path: 'no-config', - component: NoConfigComponent, + component: ConfigSetupComponent, }, { path: 'settings', diff --git a/src/app/app.service.ts b/src/app/app.service.ts index df18a3c75..5b39408c1 100644 --- a/src/app/app.service.ts +++ b/src/app/app.service.ts @@ -37,7 +37,6 @@ export class AppService { config.printer.zBabystepGCode = 'M290 Z'; config.octodash.previewProgressCircle = false; this.configService.saveConfig(config); - this.configService.updateConfig(); return true; } diff --git a/src/app/config/config.default.ts b/src/app/config/config.default.ts new file mode 100644 index 000000000..dfb237ef6 --- /dev/null +++ b/src/app/config/config.default.ts @@ -0,0 +1,107 @@ +import { Config } from './config.model'; + +export const defaultConfig: Config = { + octoprint: { + url: 'http://localhost:5000/api/', + accessToken: '', + }, + printer: { + name: '', + xySpeed: 150, + zSpeed: 5, + zBabystepGCode: 'M290 Z', + defaultTemperatureFanSpeed: { + hotend: 200, + heatbed: 60, + fan: 100, + }, + }, + filament: { + thickness: 1.75, + density: 1.25, + feedLength: 0, + feedSpeed: 20, + feedSpeedSlow: 3, + purgeDistance: 30, + useM600: false, + }, + plugins: { + displayLayerProgress: { + enabled: true, + }, + enclosure: { + enabled: false, + ambientSensorID: null, + filament1SensorID: null, + filament2SensorID: null, + }, + filamentManager: { + enabled: true, + }, + preheatButton: { + enabled: true, + }, + printTimeGenius: { + enabled: true, + }, + psuControl: { + enabled: false, + turnOnPSUWhenExitingSleep: false, + }, + }, + octodash: { + customActions: [ + { + icon: 'home', + command: 'G28', + color: '#dcdde1', + confirm: false, + exit: true, + }, + { + icon: 'ruler-vertical', + command: 'G29', + color: '#44bd32', + confirm: false, + exit: true, + }, + { + icon: 'fire-alt', + command: 'M140 S50; M104 S185', + color: '#e1b12c', + confirm: false, + exit: true, + }, + { + icon: 'snowflake', + command: 'M140 S0; M104 S0', + color: '#0097e6', + confirm: false, + exit: true, + }, + { + icon: 'redo-alt', + command: '[!RELOAD]', + color: '#7f8fa6', + confirm: true, + exit: false, + }, + { + icon: 'skull', + command: '[!KILL]', + color: '#e84118', + confirm: true, + exit: false, + }, + ], + fileSorting: { + attribute: 'name', + order: 'asc', + }, + pollingInterval: 2000, + touchscreen: true, + turnScreenOffWhileSleeping: false, + preferPreviewWhilePrinting: false, + previewProgressCircle: false, + }, +}; diff --git a/src/app/config/config.model.ts b/src/app/config/config.model.ts new file mode 100644 index 000000000..585ad15b5 --- /dev/null +++ b/src/app/config/config.model.ts @@ -0,0 +1,94 @@ +import { HttpHeaders } from '@angular/common/http'; + +export interface HttpHeader { + headers: HttpHeaders; +} + +export interface Config { + octoprint: Octoprint; + printer: Printer; + filament: Filament; + plugins: Plugins; + octodash: OctoDash; +} + +interface Octoprint { + url: string; + accessToken: string; + urlSplit?: URLSplit; +} + +export interface URLSplit { + host: string; + port: number; +} + +interface Printer { + name: string; + xySpeed: number; + zSpeed: number; + zBabystepGCode: string; + defaultTemperatureFanSpeed: DefaultTemperatureFanSpeed; +} + +interface DefaultTemperatureFanSpeed { + hotend: number; + heatbed: number; + fan: number; +} + +interface Filament { + thickness: number; + density: number; + feedLength: number; + feedSpeed: number; + feedSpeedSlow: number; + purgeDistance: number; + useM600: boolean; +} + +interface Plugins { + displayLayerProgress: Plugin; + enclosure: EnclosurePlugin; + filamentManager: Plugin; + preheatButton: Plugin; + printTimeGenius: Plugin; + psuControl: PSUControlPlugin; +} + +interface Plugin { + enabled: boolean; +} + +interface EnclosurePlugin extends Plugin { + ambientSensorID: number | null; + filament1SensorID: number | null; + filament2SensorID: number | null; +} + +interface PSUControlPlugin extends Plugin { + turnOnPSUWhenExitingSleep: boolean; +} + +interface OctoDash { + customActions: CustomAction[]; + fileSorting: FileSorting; + pollingInterval: number; + touchscreen: boolean; + turnScreenOffWhileSleeping: boolean; + preferPreviewWhilePrinting: boolean; + previewProgressCircle: boolean; +} + +export interface CustomAction { + icon: string; + command: string; + color: string; + confirm: boolean; + exit: boolean; +} + +interface FileSorting { + attribute: 'name' | 'date' | 'size'; + order: 'asc' | 'dsc'; +} diff --git a/src/app/config/config.schema.ts b/src/app/config/config.schema.ts new file mode 100644 index 000000000..df1d050ad --- /dev/null +++ b/src/app/config/config.schema.ts @@ -0,0 +1,290 @@ +export const configSchema = { + definitions: {}, + $schema: 'http://json-schema.org/draft-07/schema#', + $id: 'http://example.com/root.json', + type: 'object', + required: ['octoprint', 'printer', 'filament', 'plugins', 'octodash'], + properties: { + octoprint: { + $id: '#/properties/octoprint', + type: 'object', + required: ['accessToken', 'url'], + properties: { + accessToken: { + $id: '#/properties/octoprint/properties/accessToken', + type: 'string', + pattern: '^(.*)$', + }, + url: { + $id: '#/properties/octoprint/properties/url', + type: 'string', + pattern: '^(.*)$', + }, + }, + }, + printer: { + $id: '#/properties/printer', + type: 'object', + required: ['name', 'xySpeed', 'zSpeed', 'zBabystepGCode', 'defaultTemperatureFanSpeed'], + properties: { + name: { + $id: '#/properties/printer/properties/name', + type: 'string', + pattern: '^(.*)$', + }, + xySpeed: { + $id: '#/properties/printer/properties/xySpeed', + type: 'integer', + }, + zSpeed: { + $id: '#/properties/printer/properties/zSpeed', + type: 'integer', + }, + zBabystepGCode: { + $id: '#/properties/printer/properties/zBabystepGCode', + type: 'string', + pattern: '^(.*)$', + }, + defaultTemperatureFanSpeed: { + $id: '#/properties/printer/properties/defaultTemperatureFanSpeed', + type: 'object', + required: ['hotend', 'heatbed', 'fan'], + properties: { + hotend: { + $id: '#/properties/printer/properties/defaultTemperatureFanSpeed/hotend', + type: 'integer', + }, + heatbed: { + $id: '#/properties/printer/properties/defaultTemperatureFanSpeed/heatbed', + type: 'integer', + }, + fan: { + $id: '#/properties/printer/properties/defaultTemperatureFanSpeed/fan', + type: 'integer', + }, + }, + }, + }, + }, + filament: { + $id: '#/properties/filament', + type: 'object', + required: ['density', 'thickness', 'feedLength', 'feedSpeed', 'feedSpeedSlow', 'purgeDistance', 'useM600'], + properties: { + density: { + $id: '#/properties/filament/properties/density', + type: 'number', + }, + thickness: { + $id: '#/properties/filament/properties/thickness', + type: 'number', + }, + feedLength: { + $id: '#/properties/filament/properties/feedLength', + type: 'integer', + }, + feedSpeed: { + $id: '#/properties/filament/properties/feedSpeed', + type: 'number', + }, + feedSpeedSlow: { + $id: '#/properties/filament/properties/feedSpeedSlow', + type: 'number', + }, + purgeDistance: { + $id: '#/properties/filament/properties/purgeDistance', + type: 'integer', + }, + useM600: { + $id: '#/properties/filament/properties/useM600', + type: 'boolean', + }, + }, + }, + plugins: { + $id: '#/properties/plugins', + type: 'object', + required: [ + 'displayLayerProgress', + 'enclosure', + 'filamentManager', + 'preheatButton', + 'printTimeGenius', + 'psuControl', + ], + properties: { + displayLayerProgress: { + $id: '#/properties/plugins/properties/displayLayerProgress', + type: 'object', + required: ['enabled'], + properties: { + enabled: { + $id: '#/properties/plugins/properties/displayLayerProgress/properties/enabled', + type: 'boolean', + }, + }, + }, + enclosure: { + $id: '#/properties/plugins/properties/enclosure', + type: 'object', + required: ['enabled', 'ambientSensorID', 'filament1SensorID', 'filament2SensorID'], + properties: { + enabled: { + $id: '#/properties/plugins/properties/enclosure/properties/enabled', + type: 'boolean', + }, + ambientSensorID: { + $id: '#/properties/plugins/properties/enclosure/properties/ambientSensorID', + type: ['number', 'null'], + pattern: '^(.*)$', + }, + filament1SensorID: { + $id: '#/properties/plugins/properties/enclosure/properties/filament1SensorID', + type: ['number', 'null'], + pattern: '^(.*)$', + }, + filament2SensorID: { + $id: '#/properties/plugins/properties/enclosure/properties/filament2SensorID', + type: ['number', 'null'], + pattern: '^(.*)$', + }, + }, + }, + filamentManager: { + $id: '#/properties/plugins/properties/filamentManager', + type: 'object', + required: ['enabled'], + properties: { + enabled: { + $id: '#/properties/plugins/properties/filamentManager/properties/enabled', + type: 'boolean', + }, + }, + }, + preheatButton: { + $id: '#/properties/plugins/properties/preheatButton', + type: 'object', + required: ['enabled'], + properties: { + enabled: { + $id: '#/properties/plugins/properties/preheatButton/properties/enabled', + type: 'boolean', + }, + }, + }, + printTimeGenius: { + $id: '#/properties/plugins/properties/printTimeGenius', + type: 'object', + required: ['enabled'], + properties: { + enabled: { + $id: '#/properties/plugins/properties/printTimeGenius/properties/enabled', + type: 'boolean', + }, + }, + }, + psuControl: { + $id: '#/properties/plugins/properties/psuControl', + type: 'object', + required: ['enabled', 'turnOnPSUWhenExitingSleep'], + properties: { + enabled: { + $id: '#/properties/plugins/properties/printTimeGenius/properties/enabled', + type: 'boolean', + }, + turnOnPSUWhenExitingSleep: { + $id: '#/properties/plugins/properties/turnOnPSUWhenExitingSleep', + type: 'boolean', + }, + }, + }, + }, + }, + octodash: { + $id: '#/properties/octodash', + type: 'object', + required: [ + 'customActions', + 'fileSorting', + 'pollingInterval', + 'touchscreen', + 'turnScreenOffWhileSleeping', + 'preferPreviewWhilePrinting', + 'previewProgressCircle', + ], + properties: { + customActions: { + $id: '#/properties/octodash/properties/customActions', + type: 'array', + items: { + $id: '#/properties/octodash/properties/customActions/items', + type: 'object', + required: ['icon', 'command', 'color', 'confirm', 'exit'], + properties: { + icon: { + $id: '#/properties/octodash/properties/customActions/items/properties/icon', + type: 'string', + pattern: '^(.*)$', + }, + command: { + $id: '#/properties/octodash/properties/customActions/items/properties/command', + type: 'string', + pattern: '^(.*)$', + }, + color: { + $id: '#/properties/octodash/properties/customActions/items/properties/color', + type: 'string', + pattern: '^(.*)$', + }, + confirm: { + $id: '#/properties/octodash/properties/customActions/items/properties/confirm', + type: 'boolean', + }, + exit: { + $id: '#/properties/octodash/properties/customActions/items/properties/exit', + type: 'boolean', + }, + }, + }, + }, + fileSorting: { + $id: '#/properties/octodash/properties/fileSorting', + type: 'object', + required: ['attribute', 'order'], + properties: { + attribute: { + $id: '#/properties/octodash/properties/fileSorting/properties/attribute', + type: 'string', + pattern: '^(name|date|size)$', + }, + order: { + $id: '#/properties/octodash/properties/fileSorting/properties/order', + type: 'string', + pattern: '^(asc|dsc)$', + }, + }, + }, + pollingInterval: { + $id: '#/properties/octodash/properties/pollingInterval', + type: 'integer', + }, + touchscreen: { + $id: '#/properties/octodash/properties/touchscreen', + type: 'boolean', + }, + turnScreenOffWhileSleeping: { + $id: '#/properties/octodash/properties/turnScreenOffWhileSleeping', + type: 'boolean', + }, + preferPreviewWhilePrinting: { + $id: '#/properties/octodash/properties/preferPreviewWhilePrinting', + type: 'boolean', + }, + previewProgressCircle: { + $id: '#/properties/octodash/properties/previewProgressCircle', + type: 'boolean', + }, + }, + }, + }, +}; diff --git a/src/app/config/config.service.ts b/src/app/config/config.service.ts index 13fc7a5a3..86e00904b 100644 --- a/src/app/config/config.service.ts +++ b/src/app/config/config.service.ts @@ -4,6 +4,8 @@ import Ajv from 'ajv'; import _ from 'lodash'; import { NotificationService } from '../notification/notification.service'; +import { Config, CustomAction, HttpHeader, URLSplit } from './config.model'; +import { configSchema } from './config.schema'; @Injectable({ providedIn: 'root', @@ -22,7 +24,7 @@ export class ConfigService { public constructor(private notificationService: NotificationService) { const ajv = new Ajv({ allErrors: true }); - this.validator = ajv.compile(schema); + this.validator = ajv.compile(configSchema); try { const Store = window.require('electron-store'); this.store = new Store(); @@ -40,18 +42,22 @@ export class ConfigService { this.config = config; this.valid = this.validate(); if (this.valid) { - this.httpHeaders = { - headers: new HttpHeaders({ - 'x-api-key': this.config.octoprint.accessToken, - 'Cache-Control': 'no-cache', - Pragma: 'no-cache', - Expires: '0', - }), - }; + this.generateHttpHeaders(); } this.initialized = true; } + public generateHttpHeaders(): void { + this.httpHeaders = { + headers: new HttpHeaders({ + 'x-api-key': this.config.octoprint.accessToken, + 'Cache-Control': 'no-cache', + Pragma: 'no-cache', + Expires: '0', + }), + }; + } + public getRemoteConfig(): Config { return this.store.get('config'); } @@ -85,48 +91,44 @@ export class ConfigService { return errors; } - public saveConfig(config?: Config): string { - if (!config) { - config = this.config; - } - if (window && window.process && window.process.type) { + public saveConfig(config: Config): string { + try { this.store.set('config', config); const configStored = this.store.get('config'); if (this.validateGiven(configStored)) { + this.config = config; + this.generateHttpHeaders(); return null; } else { return 'Saved config is invalid!'; } - } else { - return "Browser version doesn't support saving!"; + } catch { + return 'Saving config failed!'; } } - public updateConfig(): void { - if (window && window.process && window.process.type) { - this.update = false; - this.initialize(this.store.get('config')); - } - } + public splitOctoprintURL(octoprintURL: string): URLSplit { + const host = octoprintURL.split(':')[1].replace('//', ''); + const port = parseInt(octoprintURL.split(':')[2].replace('/api/', ''), 10); - public revertConfigForInput(config: Config): Config { - config.octoprint.urlSplit = { - url: config.octoprint.url.split(':')[1].replace('//', ''), - port: parseInt(config.octoprint.url.split(':')[2].replace('/api/', ''), 10), + return { + host, + port: isNaN(port) ? null : port, }; - if (isNaN(config.octoprint.urlSplit.port)) { - config.octoprint.urlSplit.port = null; + } + + public mergeOctoprintURL(urlSplit: URLSplit): string { + // TODO: remove api/ from URL for v2.2.0 + if (urlSplit.port !== null || !isNaN(urlSplit.port)) { + return `http://${urlSplit.host}:${urlSplit.port}/api/`; + } else { + return `http://${urlSplit.host}/api/`; } - return config; } public createConfigFromInput(config: Config): Config { const configOut = _.cloneDeep(config); - if (config.octoprint.urlSplit.port !== null || !isNaN(config.octoprint.urlSplit.port)) { - configOut.octoprint.url = `http://${config.octoprint.urlSplit.url}:${config.octoprint.urlSplit.port}/api/`; - } else { - configOut.octoprint.url = `http://${config.octoprint.urlSplit.url}/api/`; - } + configOut.octoprint.url = this.mergeOctoprintURL(config.octoprint.urlSplit); delete configOut.octoprint.urlSplit; return configOut; } @@ -267,385 +269,3 @@ export class ConfigService { return this.config.octodash.previewProgressCircle; } } - -interface HttpHeader { - headers: HttpHeaders; -} - -export interface Config { - octoprint: Octoprint; - printer: Printer; - filament: Filament; - plugins: Plugins; - octodash: OctoDash; -} - -interface Octoprint { - url: string; - accessToken: string; - urlSplit?: { - url: string; - port: number; - }; -} - -interface Printer { - name: string; - xySpeed: number; - zSpeed: number; - zBabystepGCode: string; - defaultTemperatureFanSpeed: DefaultTemperatureFanSpeed; -} - -interface DefaultTemperatureFanSpeed { - hotend: number; - heatbed: number; - fan: number; -} - -interface Filament { - thickness: number; - density: number; - feedLength: number; - feedSpeed: number; - feedSpeedSlow: number; - purgeDistance: number; - useM600: boolean; -} - -interface Plugins { - displayLayerProgress: Plugin; - enclosure: EnclosurePlugin; - filamentManager: Plugin; - preheatButton: Plugin; - printTimeGenius: Plugin; - psuControl: PSUControlPlugin; -} - -interface Plugin { - enabled: boolean; -} - -interface EnclosurePlugin extends Plugin { - ambientSensorID: number | null; - filament1SensorID: number | null; - filament2SensorID: number | null; -} - -interface PSUControlPlugin extends Plugin { - turnOnPSUWhenExitingSleep: boolean; -} - -interface OctoDash { - customActions: CustomAction[]; - fileSorting: FileSorting; - pollingInterval: number; - touchscreen: boolean; - turnScreenOffWhileSleeping: boolean; - preferPreviewWhilePrinting: boolean; - previewProgressCircle: boolean; -} - -interface CustomAction { - icon: string; - command: string; - color: string; - confirm: boolean; - exit: boolean; -} - -interface FileSorting { - attribute: 'name' | 'date' | 'size'; - order: 'asc' | 'dsc'; -} - -const schema = { - definitions: {}, - $schema: 'http://json-schema.org/draft-07/schema#', - $id: 'http://example.com/root.json', - type: 'object', - required: ['octoprint', 'printer', 'filament', 'plugins', 'octodash'], - properties: { - octoprint: { - $id: '#/properties/octoprint', - type: 'object', - required: ['accessToken', 'url'], - properties: { - accessToken: { - $id: '#/properties/octoprint/properties/accessToken', - type: 'string', - pattern: '^(.*)$', - }, - url: { - $id: '#/properties/octoprint/properties/url', - type: 'string', - pattern: '^(.*)$', - }, - }, - }, - printer: { - $id: '#/properties/printer', - type: 'object', - required: ['name', 'xySpeed', 'zSpeed', 'zBabystepGCode', 'defaultTemperatureFanSpeed'], - properties: { - name: { - $id: '#/properties/printer/properties/name', - type: 'string', - pattern: '^(.*)$', - }, - xySpeed: { - $id: '#/properties/printer/properties/xySpeed', - type: 'integer', - }, - zSpeed: { - $id: '#/properties/printer/properties/zSpeed', - type: 'integer', - }, - zBabystepGCode: { - $id: '#/properties/printer/properties/zBabystepGCode', - type: 'string', - pattern: '^(.*)$', - }, - defaultTemperatureFanSpeed: { - $id: '#/properties/printer/properties/defaultTemperatureFanSpeed', - type: 'object', - required: ['hotend', 'heatbed', 'fan'], - properties: { - hotend: { - $id: '#/properties/printer/properties/defaultTemperatureFanSpeed/hotend', - type: 'integer', - }, - heatbed: { - $id: '#/properties/printer/properties/defaultTemperatureFanSpeed/heatbed', - type: 'integer', - }, - fan: { - $id: '#/properties/printer/properties/defaultTemperatureFanSpeed/fan', - type: 'integer', - }, - }, - }, - }, - }, - filament: { - $id: '#/properties/filament', - type: 'object', - required: ['density', 'thickness', 'feedLength', 'feedSpeed', 'feedSpeedSlow', 'purgeDistance', 'useM600'], - properties: { - density: { - $id: '#/properties/filament/properties/density', - type: 'number', - }, - thickness: { - $id: '#/properties/filament/properties/thickness', - type: 'number', - }, - feedLength: { - $id: '#/properties/filament/properties/feedLength', - type: 'integer', - }, - feedSpeed: { - $id: '#/properties/filament/properties/feedSpeed', - type: 'number', - }, - feedSpeedSlow: { - $id: '#/properties/filament/properties/feedSpeedSlow', - type: 'number', - }, - purgeDistance: { - $id: '#/properties/filament/properties/purgeDistance', - type: 'integer', - }, - useM600: { - $id: '#/properties/filament/properties/useM600', - type: 'boolean', - }, - }, - }, - plugins: { - $id: '#/properties/plugins', - type: 'object', - required: [ - 'displayLayerProgress', - 'enclosure', - 'filamentManager', - 'preheatButton', - 'printTimeGenius', - 'psuControl', - ], - properties: { - displayLayerProgress: { - $id: '#/properties/plugins/properties/displayLayerProgress', - type: 'object', - required: ['enabled'], - properties: { - enabled: { - $id: '#/properties/plugins/properties/displayLayerProgress/properties/enabled', - type: 'boolean', - }, - }, - }, - enclosure: { - $id: '#/properties/plugins/properties/enclosure', - type: 'object', - required: ['enabled', 'ambientSensorID', 'filament1SensorID', 'filament2SensorID'], - properties: { - enabled: { - $id: '#/properties/plugins/properties/enclosure/properties/enabled', - type: 'boolean', - }, - ambientSensorID: { - $id: '#/properties/plugins/properties/enclosure/properties/ambientSensorID', - type: ['number', 'null'], - pattern: '^(.*)$', - }, - filament1SensorID: { - $id: '#/properties/plugins/properties/enclosure/properties/filament1SensorID', - type: ['number', 'null'], - pattern: '^(.*)$', - }, - filament2SensorID: { - $id: '#/properties/plugins/properties/enclosure/properties/filament2SensorID', - type: ['number', 'null'], - pattern: '^(.*)$', - }, - }, - }, - filamentManager: { - $id: '#/properties/plugins/properties/filamentManager', - type: 'object', - required: ['enabled'], - properties: { - enabled: { - $id: '#/properties/plugins/properties/filamentManager/properties/enabled', - type: 'boolean', - }, - }, - }, - preheatButton: { - $id: '#/properties/plugins/properties/preheatButton', - type: 'object', - required: ['enabled'], - properties: { - enabled: { - $id: '#/properties/plugins/properties/preheatButton/properties/enabled', - type: 'boolean', - }, - }, - }, - printTimeGenius: { - $id: '#/properties/plugins/properties/printTimeGenius', - type: 'object', - required: ['enabled'], - properties: { - enabled: { - $id: '#/properties/plugins/properties/printTimeGenius/properties/enabled', - type: 'boolean', - }, - }, - }, - psuControl: { - $id: '#/properties/plugins/properties/psuControl', - type: 'object', - required: ['enabled', 'turnOnPSUWhenExitingSleep'], - properties: { - enabled: { - $id: '#/properties/plugins/properties/printTimeGenius/properties/enabled', - type: 'boolean', - }, - turnOnPSUWhenExitingSleep: { - $id: '#/properties/plugins/properties/turnOnPSUWhenExitingSleep', - type: 'boolean', - }, - }, - }, - }, - }, - octodash: { - $id: '#/properties/octodash', - type: 'object', - required: [ - 'customActions', - 'fileSorting', - 'pollingInterval', - 'touchscreen', - 'turnScreenOffWhileSleeping', - 'preferPreviewWhilePrinting', - 'previewProgressCircle', - ], - properties: { - customActions: { - $id: '#/properties/octodash/properties/customActions', - type: 'array', - items: { - $id: '#/properties/octodash/properties/customActions/items', - type: 'object', - required: ['icon', 'command', 'color', 'confirm', 'exit'], - properties: { - icon: { - $id: '#/properties/octodash/properties/customActions/items/properties/icon', - type: 'string', - pattern: '^(.*)$', - }, - command: { - $id: '#/properties/octodash/properties/customActions/items/properties/command', - type: 'string', - pattern: '^(.*)$', - }, - color: { - $id: '#/properties/octodash/properties/customActions/items/properties/color', - type: 'string', - pattern: '^(.*)$', - }, - confirm: { - $id: '#/properties/octodash/properties/customActions/items/properties/confirm', - type: 'boolean', - }, - exit: { - $id: '#/properties/octodash/properties/customActions/items/properties/exit', - type: 'boolean', - }, - }, - }, - }, - fileSorting: { - $id: '#/properties/octodash/properties/fileSorting', - type: 'object', - required: ['attribute', 'order'], - properties: { - attribute: { - $id: '#/properties/octodash/properties/fileSorting/properties/attribute', - type: 'string', - pattern: '^(name|date|size)$', - }, - order: { - $id: '#/properties/octodash/properties/fileSorting/properties/order', - type: 'string', - pattern: '^(asc|dsc)$', - }, - }, - }, - pollingInterval: { - $id: '#/properties/octodash/properties/pollingInterval', - type: 'integer', - }, - touchscreen: { - $id: '#/properties/octodash/properties/touchscreen', - type: 'boolean', - }, - turnScreenOffWhileSleeping: { - $id: '#/properties/octodash/properties/turnScreenOffWhileSleeping', - type: 'boolean', - }, - preferPreviewWhilePrinting: { - $id: '#/properties/octodash/properties/preferPreviewWhilePrinting', - type: 'boolean', - }, - previewProgressCircle: { - $id: '#/properties/octodash/properties/previewProgressCircle', - type: 'boolean', - }, - }, - }, - }, -}; diff --git a/src/app/config/invalid-config/invalid-config.component.html b/src/app/config/invalid/invalid.component.html similarity index 100% rename from src/app/config/invalid-config/invalid-config.component.html rename to src/app/config/invalid/invalid.component.html diff --git a/src/app/config/invalid-config/invalid-config.component.scss b/src/app/config/invalid/invalid.component.scss similarity index 100% rename from src/app/config/invalid-config/invalid-config.component.scss rename to src/app/config/invalid/invalid.component.scss diff --git a/src/app/config/invalid-config/invalid-config.component.ts b/src/app/config/invalid/invalid.component.ts similarity index 60% rename from src/app/config/invalid-config/invalid-config.component.ts rename to src/app/config/invalid/invalid.component.ts index a3b864881..976caf5bc 100644 --- a/src/app/config/invalid-config/invalid-config.component.ts +++ b/src/app/config/invalid/invalid.component.ts @@ -3,11 +3,11 @@ import { Component, OnInit } from '@angular/core'; import { ConfigService } from '../config.service'; @Component({ - selector: 'app-invalid-config', - templateUrl: './invalid-config.component.html', - styleUrls: ['./invalid-config.component.scss'], + selector: 'app-config-invalid', + templateUrl: './invalid.component.html', + styleUrls: ['./invalid.component.scss'], }) -export class InvalidConfigComponent implements OnInit { +export class ConfigInvalidComponent implements OnInit { public errors: string[]; public constructor(private configService: ConfigService) {} diff --git a/src/app/config/no-config/no-config.component.html b/src/app/config/no-config/no-config.component.html deleted file mode 100644 index 01f7c7838..000000000 --- a/src/app/config/no-config/no-config.component.html +++ /dev/null @@ -1,358 +0,0 @@ -
- - - - - - -
-
- - back -
-
-
-
-
-
-
- next - -
-
- -
- Hey there! - - It looks like this is the first start of OctoDash. -
- I'll help you set up your config and get you started. -
- If you encounter any issues please check the troubleshooting guide in the GitHub Wiki. -
-
- Thanks for choosing OctoDash - -
- - Sorry for bothering you again ... -
- We've released an update, so awesome, we needed to change the config. Please review your new config! -
-
- Thanks for choosing OctoDash - -
-
- -
- - First things first: Please select your OctoPrint instance from the list below or enter the IP/URL manually. - -
-
- -
- {{ node.name }} -
- - Version {{ node.version }}, URL: {{ node.url.replace('/api/', '') }} - -
- -
- searching -
-
enter manually
-
-
-
- http:// - - : - -

- Port 5000 for vanilla OctoPrint, Port 80 for OctoPi -

-
-
- search again -
-
-
- -
- connecting -
- -
- - Please authenticate me now. You can either click the button below and confirm the request in your OctoPrint - webinterface or enter an API Key manually. - - -
- - -
-
- -
- - Now tell me some facts about your printer so I can personalize OctoDash for you. - -
- - -
-
- If you're unsure about any values during setup, just stick with the defaults. You can change all values (and even - more stuff) in the settings menu anytime. There is also a description of each attribute available in the GitHub - Wiki. -
-
-
- I also need some information about your extruder. -
- -
- - - - - - - + - - mm -
-
- -
- - - - - - - + - - mm/s -
- -
- These values will be used during the Filament Change Process. Make sure to have your Feed Length (distance - Extruder to Hotend) and Feed Speed configured correctly. -
-
-
-
- And now personalize me to your liking. -
-
- - - - Use Touchscreen -
-
- - - - - ms -
- Don't set your Value Refresh Interval too low (und 1000ms) as this may effect the performance of the Raspberry - Pi. -
-
-
-
- What plugins are you running? -
-
- - - - Display Layer Progress   -
-
- - - - Enclosure -
-
- - - - Filament Manager   -
-
- - - - Preheat Button -
-
- - - - Print Time Genius   -
-
- - - - PSU Control -
-
- Enclosure and PSU Control Plugin can be configured further in the settings. -
-
-
-
- Great! I'll check everything. -
- - - Octoprint Connection - - - - - Validating Config - - {{ error }} -
- - - - Saving Config - - {{ configSaved }} -
- done -
-
-
diff --git a/src/app/config/no-config/no-config.component.ts b/src/app/config/no-config/no-config.component.ts deleted file mode 100644 index 74cf0d05c..000000000 --- a/src/app/config/no-config/no-config.component.ts +++ /dev/null @@ -1,353 +0,0 @@ -import { Component, NgZone, OnInit } from '@angular/core'; -import { Router } from '@angular/router'; -import { ElectronService } from 'ngx-electron'; -import { NotificationService } from 'src/app/notification/notification.service'; -import { OctoprintScriptService } from 'src/app/octoprint-script.service'; - -import { Config, ConfigService } from '../config.service'; - -@Component({ - selector: 'app-no-config', - templateUrl: './no-config.component.html', - styleUrls: ['./no-config.component.scss'], -}) -export class NoConfigComponent implements OnInit { - public page = 0; - public totalPages = 7; - - public configUpdate: boolean; - public config: Config; - public configErrors: string[]; - public configValid: boolean; - public configSaved: string; - - public manualURL = false; - public octoprintNodes: OctoprintNodes; - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - private OctoPrint: any; - - public octoprintConnection = false; - public octoprintConnectionError: string; - - public constructor( - private configService: ConfigService, - private router: Router, - private notificationService: NotificationService, - private electronService: ElectronService, - private octoprintScriptService: OctoprintScriptService, - private ngZone: NgZone, - ) { - this.configUpdate = this.configService.isUpdate(); - if (this.configUpdate) { - this.config = configService.getCurrentConfig(); - } else { - this.config = this.getDefaultConfig(); - } - this.config = this.configService.revertConfigForInput(this.config); - } - - public ngOnInit(): void { - this.changeProgress(); - - this.electronService.ipcRenderer.on('discoveredNodes', (_, nodes: OctoprintNodes) => { - this.ngZone.run(() => { - this.octoprintNodes = nodes; - }); - }); - } - - public discoverOctoprintInstances(): void { - this.octoprintNodes = null; - this.electronService.ipcRenderer.send('discover'); - setTimeout(() => { - const searching = document.querySelector('.no-config__discovered-instances__searching'); - if (searching) { - searching.innerHTML = 'no instances found.'; - searching.classList.remove('loading-dots'); - } - }, 10000); - } - - public setOctoprintInstance(node: OctoprintNodes): void { - this.config.octoprint.url = node.url; - this.config = this.configService.revertConfigForInput(this.config); - this.increasePage(); - } - - public enterURLManually(): void { - this.config.octoprint.urlSplit = { - url: 'localhost', - port: 5000, - }; - this.manualURL = true; - } - - private async loadOctoprintClient() { - this.octoprintScriptService - .downloadScript(`http://${this.config.octoprint.urlSplit.url}:${this.config.octoprint.urlSplit.port}/api/`) - .then(() => { - this.OctoPrint = this.octoprintScriptService.getInstance(); - this.changePage(0.5); - }) - .catch(() => { - this.notificationService.setError( - "Can't connect to OctoPrint!", - `Check the URL/IP and make sure that your firewall allows access to port ${this.config.octoprint.urlSplit.port} on host ${this.config.octoprint.urlSplit.url}.`, - ); - this.changePage(-0.5); - }); - } - - public loginWithOctoPrintUI(): void { - this.notificationService.setNotification( - 'Login request send!', - 'Please confirm the request via the popup in the OctoPrint WebUI.', - ); - - const closeInfo = setTimeout(() => { - this.notificationService.closeNotification(); - }, 3000); - - this.OctoPrint.plugins.appkeys - .authenticate('OctoDash') - .done((apiKey: string) => { - this.config.octoprint.accessToken = apiKey; - this.octoprintScriptService.authenticate(apiKey); - this.OctoPrint = this.octoprintScriptService.getInstance(); - - // FIXME: to be changed - this.OctoPrint.printerprofiles - .list() - .done(profiles => { - this.config.printer.name = profiles.profiles._default.name; - }) - .fail(() => console.error('ERR')); - // END - - setTimeout(() => { - this.increasePage(); - }, 600); - }) - .fail(() => { - this.notificationService.setWarning( - 'Something went wrong!', - "Can' retrieve the API Key, please try again or create one manually and enter it down below.", - ); - }) - .always(() => { - clearTimeout(closeInfo); - }); - } - - changeFeedLength(amount: number): void { - if (this.config.filament.feedLength + amount < 0) { - this.config.filament.feedLength = 0; - } else if (this.config.filament.feedLength + amount > 9999) { - this.config.filament.feedLength = 9999; - } else { - this.config.filament.feedLength += amount; - } - } - - changeFeedSpeed(amount: number): void { - if (this.config.filament.feedSpeed + amount < 0) { - this.config.filament.feedSpeed = 0; - } else if (this.config.filament.feedSpeed + amount > 999) { - this.config.filament.feedSpeed = 999; - } else { - this.config.filament.feedSpeed += amount; - } - } - - public createConfig(): void { - this.configErrors = []; - this.octoprintConnectionError = null; - this.config = this.configService.createConfigFromInput(this.config); - this.validateConfig(); - } - - public async validateConfig(): Promise { - this.configValid = this.configService.validateGiven(this.config); - if (!this.configValid) { - this.configErrors = this.configService.getErrors(); - } else { - this.saveConfig(); - } - } - - public saveConfig(): void { - this.configSaved = this.configService.saveConfig(this.config); - } - - public finishWizard(): void { - this.configService.updateConfig(); - this.router.navigate(['/main-screen']); - } - - private changePage(value: number): void { - if (this.page + value > this.totalPages || this.page + value < 0) { - return; - } - this.page = this.page + this.beforeNavigation(value); - this.afterNavigation(); - this.changeProgress(); - } - - private beforeNavigation(value: number): number { - switch (this.page) { - case 1: - this.electronService.ipcRenderer.send('stopDiscover'); - if (value > 0) { - this.loadOctoprintClient(); - return 0.5; - } - break; - case this.totalPages - 1: - if (value < 0) { - this.config = this.configService.revertConfigForInput(this.config); - } - break; - } - return value; - } - - private afterNavigation(): void { - switch (this.page) { - case 1: - this.discoverOctoprintInstances(); - break; - case this.totalPages: - this.createConfig(); - break; - } - } - - public increasePage(): void { - this.changePage(1); - } - - public decreasePage(): void { - this.changePage(-1); - } - - public changeProgress(): void { - document.getElementById('progressBar').style.width = this.page * (20 / this.totalPages) + 'vw'; - } - - public getDefaultConfig(): Config { - return { - octoprint: { - url: 'http://localhost:5000/api/', - accessToken: '', - }, - printer: { - name: '', - xySpeed: 150, - zSpeed: 5, - zBabystepGCode: 'M290 Z', - defaultTemperatureFanSpeed: { - hotend: 200, - heatbed: 60, - fan: 100, - }, - }, - filament: { - thickness: 1.75, - density: 1.25, - feedLength: 0, - feedSpeed: 20, - feedSpeedSlow: 3, - purgeDistance: 30, - useM600: false, - }, - plugins: { - displayLayerProgress: { - enabled: true, - }, - enclosure: { - enabled: false, - ambientSensorID: null, - filament1SensorID: null, - filament2SensorID: null, - }, - filamentManager: { - enabled: true, - }, - preheatButton: { - enabled: true, - }, - printTimeGenius: { - enabled: true, - }, - psuControl: { - enabled: false, - turnOnPSUWhenExitingSleep: false, - }, - }, - octodash: { - customActions: [ - { - icon: 'home', - command: 'G28', - color: '#dcdde1', - confirm: false, - exit: true, - }, - { - icon: 'ruler-vertical', - command: 'G29', - color: '#44bd32', - confirm: false, - exit: true, - }, - { - icon: 'fire-alt', - command: 'M140 S50; M104 S185', - color: '#e1b12c', - confirm: false, - exit: true, - }, - { - icon: 'snowflake', - command: 'M140 S0; M104 S0', - color: '#0097e6', - confirm: false, - exit: true, - }, - { - icon: 'redo-alt', - command: '[!RELOAD]', - color: '#7f8fa6', - confirm: true, - exit: false, - }, - { - icon: 'skull', - command: '[!KILL]', - color: '#e84118', - confirm: true, - exit: false, - }, - ], - fileSorting: { - attribute: 'name', - order: 'asc', - }, - pollingInterval: 2000, - touchscreen: true, - turnScreenOffWhileSleeping: false, - preferPreviewWhilePrinting: false, - previewProgressCircle: false, - }, - }; - } -} - -interface OctoprintNodes { - id: number; - name: string; - version: string; - url: string; - disable: boolean; -} diff --git a/src/app/config/setup/discover-octoprint/discover-octoprint.component.html b/src/app/config/setup/discover-octoprint/discover-octoprint.component.html new file mode 100644 index 000000000..cb0efc4e6 --- /dev/null +++ b/src/app/config/setup/discover-octoprint/discover-octoprint.component.html @@ -0,0 +1,47 @@ + + First things first: Please select your OctoPrint instance from the list below or enter the IP/URL manually. + +
+
+ +
+ {{ node.name }} +
+ + Version {{ node.version }}, URL: {{ node.url.replace('/api/', '') }} + +
+ +
+ searching +
+
enter manually
+
+
+
+ http:// + + : + +

Port 5000 for vanilla OctoPrint, Port 80 for OctoPi

+
+
search again
+
diff --git a/src/app/config/setup/discover-octoprint/discover-octoprint.component.scss b/src/app/config/setup/discover-octoprint/discover-octoprint.component.scss new file mode 100644 index 000000000..157079b8a --- /dev/null +++ b/src/app/config/setup/discover-octoprint/discover-octoprint.component.scss @@ -0,0 +1,49 @@ +.discover-octoprint { + &__wrapper { + height: 40vh; + padding: 4vh 3vw; + margin-right: 2vw; + overflow-y: auto; + overflow-x: hidden; + } + + &__searching { + display: block; + text-align: center; + margin-top: 10vh; + font-size: 0.8rem; + } + + &__node { + display: block; + background-color: #718093; + border-radius: 1vw; + margin-bottom: 2vh; + padding: 2vh 2vw; + + &-name { + white-space: nowrap; + text-overflow: ellipsis; + font-size: 0.8rem; + float: left; + width: 90%; + } + + &-details { + font-size: 0.55rem; + opacity: 0.7; + } + + &-connect-icon { + height: 10vh; + float: right; + margin-top: 1.8vh; + } + } + + &__manual { + text-align: center; + font-size: 0.7rem; + line-height: 14vh; + } +} diff --git a/src/app/config/setup/discover-octoprint/discover-octoprint.component.ts b/src/app/config/setup/discover-octoprint/discover-octoprint.component.ts new file mode 100644 index 000000000..d3f6160ff --- /dev/null +++ b/src/app/config/setup/discover-octoprint/discover-octoprint.component.ts @@ -0,0 +1,75 @@ +import { Component, EventEmitter, Input, NgZone, OnDestroy, OnInit, Output } from '@angular/core'; +import { ElectronService } from 'ngx-electron'; + +import { ConfigService } from '../../config.service'; + +@Component({ + selector: 'app-config-setup-discover-octoprint', + templateUrl: './discover-octoprint.component.html', + styleUrls: ['./discover-octoprint.component.scss', '../setup.component.scss'], +}) +export class DiscoverOctoprintComponent implements OnInit, OnDestroy { + @Input() octoprintHost: number; + @Input() octoprintPort: number; + + @Output() increasePage = new EventEmitter(); + @Output() changeURLEntryMethod = new EventEmitter(); + @Output() octoprintHostChange = new EventEmitter(); + @Output() octoprintPortChange = new EventEmitter(); + + public manualURL = false; + public octoprintNodes: OctoprintNodes; + + constructor(private configService: ConfigService, private electronService: ElectronService, private zone: NgZone) {} + + ngOnInit(): void { + this.electronService.ipcRenderer.on('discoveredNodes', (_, nodes: OctoprintNodes) => { + this.zone.run(() => { + this.octoprintNodes = nodes; + }); + }); + + this.discoverOctoprintInstances(); + } + + ngOnDestroy(): void { + this.electronService.ipcRenderer.send('stopDiscover'); + } + + private discoverOctoprintInstances(): void { + this.octoprintNodes = null; + this.electronService.ipcRenderer.send('discover'); + setTimeout(() => { + const searching = document.querySelector('.discover-octoprint__searching'); + if (searching) { + searching.innerHTML = 'no instances found.'; + searching.classList.remove('loading-dots'); + } + }, 10000); + } + + public setOctoprintInstance(node: OctoprintNodes): void { + const urlSplit = this.configService.splitOctoprintURL(node.url); + this.octoprintHostChange.emit(urlSplit.host); + this.octoprintPortChange.emit(urlSplit.port); + this.increasePage.emit(); + } + + public searchForInstance(): void { + this.manualURL = false; + this.changeURLEntryMethod.emit(this.manualURL); + } + + public enterURLManually(): void { + this.manualURL = true; + this.changeURLEntryMethod.emit(this.manualURL); + } +} + +interface OctoprintNodes { + id: number; + name: string; + version: string; + url: string; + disable: boolean; +} diff --git a/src/app/config/setup/extruder-information/extruder-information.component.html b/src/app/config/setup/extruder-information/extruder-information.component.html new file mode 100644 index 000000000..6be50b93f --- /dev/null +++ b/src/app/config/setup/extruder-information/extruder-information.component.html @@ -0,0 +1,91 @@ +I also need some information about your extruder. +
+ +
+ + - + + + + + + + mm +
+
+ +
+ + - + + + + + + + mm/s +
+ +
+ These values are used for filament changing. Make sure to have your feed length (distance extruder to hotend) and + feed speed configured correctly. +
+
diff --git a/src/app/config/setup/extruder-information/extruder-information.component.scss b/src/app/config/setup/extruder-information/extruder-information.component.scss new file mode 100644 index 000000000..f3ded5841 --- /dev/null +++ b/src/app/config/setup/extruder-information/extruder-information.component.scss @@ -0,0 +1,7 @@ +form { + margin-left: 4vw; +} + +.setup__explanation { + margin-top: 4vh !important; +} diff --git a/src/app/config/setup/extruder-information/extruder-information.component.ts b/src/app/config/setup/extruder-information/extruder-information.component.ts new file mode 100644 index 000000000..92d2018da --- /dev/null +++ b/src/app/config/setup/extruder-information/extruder-information.component.ts @@ -0,0 +1,36 @@ +import { Component, EventEmitter, Input, Output } from '@angular/core'; + +@Component({ + selector: 'app-config-setup-extruder-information', + templateUrl: './extruder-information.component.html', + styleUrls: ['./extruder-information.component.scss', '../setup.component.scss'], +}) +export class ExtruderInformationComponent { + @Input() feedLength: number; + @Input() feedSpeed: number; + + @Output() feedLengthChange = new EventEmitter(); + @Output() feedSpeedChange = new EventEmitter(); + + changeFeedLength(amount: number): void { + if (this.feedLength + amount < 0) { + this.feedLength = 0; + } else if (this.feedLength + amount > 9999) { + this.feedLength = 9999; + } else { + this.feedLength += amount; + } + this.feedLengthChange.emit(this.feedLength); + } + + changeFeedSpeed(amount: number): void { + if (this.feedSpeed + amount < 0) { + this.feedSpeed = 0; + } else if (this.feedSpeed + amount > 999) { + this.feedSpeed = 999; + } else { + this.feedSpeed += amount; + } + this.feedSpeedChange.emit(this.feedSpeed); + } +} diff --git a/src/app/config/setup/octoprint-authentication/octoprint-authentication.component.html b/src/app/config/setup/octoprint-authentication/octoprint-authentication.component.html new file mode 100644 index 000000000..2029b0f62 --- /dev/null +++ b/src/app/config/setup/octoprint-authentication/octoprint-authentication.component.html @@ -0,0 +1,20 @@ + + Please authenticate me now. You can either click the button below and confirm the request in your OctoPrint + webinterface or enter an API Key manually. + + +
+ + +
diff --git a/src/app/config/setup/octoprint-authentication/octoprint-authentication.component.scss b/src/app/config/setup/octoprint-authentication/octoprint-authentication.component.scss new file mode 100644 index 000000000..afc17ed44 --- /dev/null +++ b/src/app/config/setup/octoprint-authentication/octoprint-authentication.component.scss @@ -0,0 +1,6 @@ +.octoprint-authentication { + &__login-button { + text-align: center; + padding: 8vh 0 0; + } +} diff --git a/src/app/config/setup/octoprint-authentication/octoprint-authentication.component.ts b/src/app/config/setup/octoprint-authentication/octoprint-authentication.component.ts new file mode 100644 index 000000000..2987992ea --- /dev/null +++ b/src/app/config/setup/octoprint-authentication/octoprint-authentication.component.ts @@ -0,0 +1,94 @@ +import { HttpResponse } from '@angular/common/http'; +import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { interval } from 'rxjs'; +import { NotificationService } from 'src/app/notification/notification.service'; +import { AuthService, TokenSuccess } from 'src/app/octoprint/auth.service'; + +@Component({ + selector: 'app-config-setup-octoprint-authentication', + templateUrl: './octoprint-authentication.component.html', + styleUrls: ['./octoprint-authentication.component.scss', '../setup.component.scss'], +}) +export class OctoprintAuthenticationComponent { + @Input() octoprintURL: string; + @Input() accessToken: string; + + @Output() increasePage = new EventEmitter(); + @Output() accessTokenChange = new EventEmitter(); + + constructor(private authService: AuthService, private notificationService: NotificationService) {} + + public loginWithOctoprintUI(): void { + this.authService.probeSupport(this.octoprintURL).subscribe( + result => { + if (result.status === 204) { + this.sendLoginRequest(); + } else { + this.notificationService.setWarning( + 'Automatic login not supported!', + `Please create the API Key manually and paste it in the bottom field.`, + ); + } + }, + error => { + if (error.status === 0) { + this.notificationService.setError( + "Can't connect to OctoPrint!", + `Check the URL/IP and make sure that your OctoPrint instance is reachable from this device.`, + ); + } else { + this.notificationService.setWarning( + 'Automatic Login not supported!', + `Please create the API Key manually and paste it in the bottom field.`, + ); + } + }, + ); + } + + private sendLoginRequest(): void { + this.authService.startProcess(this.octoprintURL).subscribe( + result => { + this.notificationService.setNotification( + 'Login request send!', + 'Please confirm the request via the popup in the OctoPrint WebUI.', + ); + this.pollResult(result.app_token); + }, + _ => { + this.notificationService.setWarning( + 'Automatic Login failed!', + `Please try again or create the API Key manually and paste it in the bottom field.`, + ); + }, + ); + } + + private pollResult(token: string): void { + setTimeout(() => { + this.notificationService.closeNotification(); + }, 2000); + const pollInterval = interval(1000).subscribe(() => { + this.authService.pollStatus(this.octoprintURL, token).subscribe( + result => { + if (result.status === 200) { + pollInterval.unsubscribe(); + const resultSuccess = result as HttpResponse; + this.accessToken = resultSuccess.body.api_key; + this.accessTokenChange.emit(resultSuccess.body.api_key); + setTimeout(() => { + this.increasePage.emit(); + }, 600); + } + }, + _ => { + this.notificationService.setWarning( + 'Automatic Login failed!', + `Please try again or create the API Key manually and paste it in the bottom field.`, + ); + pollInterval.unsubscribe(); + }, + ); + }); + } +} diff --git a/src/app/config/setup/personalization/personalization.component.html b/src/app/config/setup/personalization/personalization.component.html new file mode 100644 index 000000000..09520485d --- /dev/null +++ b/src/app/config/setup/personalization/personalization.component.html @@ -0,0 +1,24 @@ + Now tell me some facts about your setup so I can personalize OctoDash for you. +
+ + +
+ + + + Use Touchscreen +
+
+
+ You can change all settings (and even more) in the settings menu anytime. There is also a description of each + attribute available in the GitHub Wiki. +
diff --git a/src/app/config/setup/personalization/personalization.component.scss b/src/app/config/setup/personalization/personalization.component.scss new file mode 100644 index 000000000..29e0cb090 --- /dev/null +++ b/src/app/config/setup/personalization/personalization.component.scss @@ -0,0 +1,5 @@ +.personalization { + &__input { + margin-top: 5vh; + } +} diff --git a/src/app/config/setup/personalization/personalization.component.ts b/src/app/config/setup/personalization/personalization.component.ts new file mode 100644 index 000000000..c5480ec10 --- /dev/null +++ b/src/app/config/setup/personalization/personalization.component.ts @@ -0,0 +1,33 @@ +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { PrinterProfileService } from 'src/app/printerprofile.service'; + +@Component({ + selector: 'app-config-setup-personalization', + templateUrl: './personalization.component.html', + styleUrls: ['./personalization.component.scss', '../setup.component.scss'], +}) +export class PersonalizationComponent implements OnInit { + @Input() printerName: string; + @Input() useTouchscreen: boolean; + @Input() octoprintURL: string; + @Input() apiKey: string; + + @Output() printerNameChange = new EventEmitter(); + @Output() useTouchscreenChange = new EventEmitter(); + + constructor(private printerProfileService: PrinterProfileService) {} + + ngOnInit(): void { + this.printerProfileService.getActivePrinterProfileName(this.octoprintURL, this.apiKey).subscribe(printerName => { + if (!this.printerName) { + this.printerName = printerName; + this.printerNameChange.emit(this.printerName); + } + }); + } + + changeUseTouchscreen(): void { + this.useTouchscreen = !this.useTouchscreen; + this.useTouchscreenChange.emit(this.useTouchscreen); + } +} diff --git a/src/app/config/setup/plugins/plugins.component.html b/src/app/config/setup/plugins/plugins.component.html new file mode 100644 index 000000000..cccef3019 --- /dev/null +++ b/src/app/config/setup/plugins/plugins.component.html @@ -0,0 +1,40 @@ +What plugins are you running? +
+
+ + + + Display Layer Progress   +
+
+ + + + Enclosure +
+
+ + + + Filament Manager   +
+
+ + + + Preheat Button +
+
+ + + + Print Time Genius   +
+
+ + + + PSU Control +
+
Enclosure and PSU Control Plugin can be configured further in the settings.
+
diff --git a/src/app/config/setup/plugins/plugins.component.scss b/src/app/config/setup/plugins/plugins.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/config/setup/plugins/plugins.component.ts b/src/app/config/setup/plugins/plugins.component.ts new file mode 100644 index 000000000..d11ed678f --- /dev/null +++ b/src/app/config/setup/plugins/plugins.component.ts @@ -0,0 +1,52 @@ +import { Component, EventEmitter, Input, Output } from '@angular/core'; + +@Component({ + selector: 'app-config-setup-plugins', + templateUrl: './plugins.component.html', + styleUrls: ['./plugins.component.scss', '../setup.component.scss'], +}) +export class PluginsComponent { + @Input() displayLayerProgressPlugin: boolean; + @Input() enclosurePlugin: boolean; + @Input() filamentManagerPlugin: boolean; + @Input() preheatButtonPlugin: boolean; + @Input() printTimeGeniusPlugin: boolean; + @Input() psuControlPlugin: boolean; + + @Output() displayLayerProgressPluginChange = new EventEmitter(); + @Output() enclosurePluginChange = new EventEmitter(); + @Output() filamentManagerPluginChange = new EventEmitter(); + @Output() preheatButtonPluginChange = new EventEmitter(); + @Output() printTimeGeniusPluginChange = new EventEmitter(); + @Output() psuControlPluginChange = new EventEmitter(); + + public changeDisplayLayerProgressPlugin(): void { + this.displayLayerProgressPlugin = !this.displayLayerProgressPlugin; + this.displayLayerProgressPluginChange.emit(this.displayLayerProgressPlugin); + } + + public changeEnclosurePlugin(): void { + this.enclosurePlugin = !this.enclosurePlugin; + this.enclosurePluginChange.emit(this.enclosurePlugin); + } + + public changeFilamentManagerPlugin(): void { + this.filamentManagerPlugin = !this.filamentManagerPlugin; + this.filamentManagerPluginChange.emit(this.filamentManagerPlugin); + } + + public changePreheatButtonPlugin(): void { + this.preheatButtonPlugin = !this.preheatButtonPlugin; + this.preheatButtonPluginChange.emit(this.preheatButtonPlugin); + } + + public changePrintTimeGeniusPlugin(): void { + this.printTimeGeniusPlugin = !this.printTimeGeniusPlugin; + this.printTimeGeniusPluginChange.emit(this.printTimeGeniusPlugin); + } + + public changePsuControlPlugin(): void { + this.psuControlPlugin = !this.psuControlPlugin; + this.psuControlPluginChange.emit(this.psuControlPlugin); + } +} diff --git a/src/app/config/setup/setup.component.html b/src/app/config/setup/setup.component.html new file mode 100644 index 000000000..2897ce082 --- /dev/null +++ b/src/app/config/setup/setup.component.html @@ -0,0 +1,94 @@ +
+ + + + + + +
+
+ + back +
+
+
+
+
+
+
+ next + +
+
+ + + + + + + + + + + + + +
+ Great! I'll check everything. +
+ + + Octoprint Connection + +
+ + + + Validating Config + +
+
+ + + + Saving Config + + {{ configSaved }} +
+ {{ error }} + done +
+
+
diff --git a/src/app/config/no-config/no-config.component.scss b/src/app/config/setup/setup.component.scss similarity index 67% rename from src/app/config/no-config/no-config.component.scss rename to src/app/config/setup/setup.component.scss index a7a17ba12..d686f64cb 100644 --- a/src/app/config/no-config/no-config.component.scss +++ b/src/app/config/setup/setup.component.scss @@ -1,17 +1,4 @@ -.no-config { - &__text { - display: block; - text-align: center; - margin-left: 2vh; - margin-right: 3vh; - font-size: 3.6vw; - - fa-icon { - display: block; - margin-top: 2vh; - } - } - +.setup { &__progress-bar { height: 4vh; border-radius: 2vh; @@ -28,6 +15,27 @@ } } + &__text { + display: block; + text-align: center; + margin-left: 2vh; + margin-right: 3vh; + font-size: 3.6vw; + + fa-icon { + display: block; + margin-top: 2vh; + } + } + + &__button { + background-color: #44bd32; + display: inline-block; + padding: 1.4vh 2vw; + border-radius: 1vw; + box-shadow: 0 10px 19px -8px rgba(0, 0, 0, 0.75); + } + &__input { background: transparent; margin-top: 7vh; @@ -140,93 +148,16 @@ text-align: center; } - &-explanation { + &__explanation { font-size: 3vw; font-style: italic; opacity: 0.7; - margin-top: 4vh; + margin-top: 6vh; padding: 2vh 4vw; text-align: center; } - &__discovered-instances { - &__wrapper { - height: 40vh; - padding: 4vh 3vw; - margin-right: 2vw; - overflow-y: auto; - overflow-x: hidden; - } - - &__searching { - display: block; - text-align: center; - margin-top: 10vh; - font-size: 0.8rem; - } - - &__node { - display: block; - background-color: #718093; - border-radius: 1vw; - margin-bottom: 2vh; - padding: 2vh 2vw; - - &-name { - white-space: nowrap; - text-overflow: ellipsis; - font-size: 0.8rem; - float: left; - width: 90%; - } - - &-details { - font-size: 0.55rem; - opacity: 0.7; - } - - &-connect-icon { - height: 10vh; - float: right; - margin-top: 1.8vh; - } - } - - &__manual { - text-align: center; - font-size: 0.7rem; - line-height: 14vh; - } - } - - &__login-request-button-wrapper { - text-align: center; - padding: 8vh 0 0; - } - - &__1 { - &-welcome { - display: block; - text-align: center; - font-size: 6vw; - margin-top: 2vh; - margin-bottom: 6vh; - font-weight: 500; - } - } - - &__2 { - &-form { - text-align: left; - margin-left: 4vw; - } - - &-input { - margin-top: 5vh; - } - } - - &__6 { + &__validation { &-check-wrapper { width: 100%; text-align: center; @@ -250,14 +181,6 @@ display: block; margin-bottom: 4vh; } - - &-finish { - background-color: #44bd32; - display: inline-block; - padding: 1.4vh 2vw; - border-radius: 1vw; - box-shadow: 0 10px 19px -8px rgba(0, 0, 0, 0.75); - } } &__connecting { diff --git a/src/app/config/setup/setup.component.ts b/src/app/config/setup/setup.component.ts new file mode 100644 index 000000000..8af1ce717 --- /dev/null +++ b/src/app/config/setup/setup.component.ts @@ -0,0 +1,124 @@ +import { HttpClient, HttpErrorResponse, HttpHeaders } from '@angular/common/http'; +import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; + +import { defaultConfig } from '../config.default'; +import { Config } from '../config.model'; +import { ConfigService } from '../config.service'; + +@Component({ + selector: 'app-config-setup', + templateUrl: './setup.component.html', + styleUrls: ['./setup.component.scss'], +}) +export class ConfigSetupComponent implements OnInit { + public page = 0; + public totalPages = 6; + + public configUpdate: boolean; + public config: Config; + + public octoprintConnection = false; + public configValid = false; + public configSaved = 'no'; + public configErrors: string[]; + + public manualURL = false; + + public constructor(private configService: ConfigService, private http: HttpClient, private router: Router) { + this.configUpdate = this.configService.isUpdate(); + if (this.configUpdate) { + this.config = configService.getCurrentConfig(); + } else { + this.config = defaultConfig; + } + this.config.octoprint.urlSplit = this.configService.splitOctoprintURL(this.config.octoprint.url); + } + + public ngOnInit(): void { + this.changeProgress(); + } + + public changeURLEntryMethod(manual: boolean): void { + this.manualURL = manual; + } + + public getOctoprintURL(): string { + return this.configService.mergeOctoprintURL(this.config.octoprint.urlSplit); + } + + public createConfig(): void { + this.configErrors = []; + this.config = this.configService.createConfigFromInput(this.config); + this.checkOctoPrintConnection(); + } + + private checkOctoPrintConnection(): void { + const httpHeaders = { + headers: new HttpHeaders({ + 'x-api-key': this.config.octoprint.accessToken, + }), + }; + this.http.get(this.config.octoprint.url + 'version', httpHeaders).subscribe( + (): void => { + this.octoprintConnection = true; + this.validateConfig(); + }, + (error: HttpErrorResponse): void => { + this.octoprintConnection = false; + if (error.message.includes('403 FORBIDDEN')) { + this.configErrors.push("403 Forbidden - This most likely means that your API Key isn't working."); + } else if (error.message.includes('0 Unknown Error')) { + this.configErrors.push( + "0 Unknown Error - This most likely means that your OctoPrint host and port aren't correct.", + ); + } else { + this.configErrors.push(error.message); + } + }, + ); + } + + private async validateConfig(): Promise { + this.configValid = this.configService.validateGiven(this.config); + if (!this.configValid) { + this.configErrors = this.configService.getErrors(); + } else { + this.saveConfig(); + } + } + + private saveConfig(): void { + this.configSaved = this.configService.saveConfig(this.config); + } + + public finishWizard(): void { + this.router.navigate(['/main-screen']); + } + + private changePage(value: number): void { + if (this.page + value > this.totalPages || this.page + value < 0) { + return; + } + this.page += value; + this.changeProgress(); + } + + public increasePage(): void { + if (this.page === this.totalPages - 1) { + this.createConfig(); + } + this.changePage(1); + } + + public decreasePage(): void { + if (this.page === this.totalPages) { + this.config.octoprint.urlSplit = this.configService.splitOctoprintURL(this.config.octoprint.url); + } + this.changePage(-1); + } + + public changeProgress(): void { + document.getElementById('progressBar').style.width = this.page * (20 / this.totalPages) + 'vw'; + } +} diff --git a/src/app/config/setup/welcome/welcome.component.html b/src/app/config/setup/welcome/welcome.component.html new file mode 100644 index 000000000..69d35cf73 --- /dev/null +++ b/src/app/config/setup/welcome/welcome.component.html @@ -0,0 +1,21 @@ +Hey there! + + It looks like this is the first start of OctoDash. +
+ I'll help you set up your config and get you started. +
+ If you encounter any issues please check the troubleshooting guide in the GitHub Wiki. +
+
+ Thanks for choosing OctoDash + +
+ + Sorry for bothering you again ... +
+ We've released an update, so awesome, we needed to change the config. Please review your new config! +
+
+ Thanks for choosing OctoDash + +
diff --git a/src/app/config/setup/welcome/welcome.component.scss b/src/app/config/setup/welcome/welcome.component.scss new file mode 100644 index 000000000..c0a7a366f --- /dev/null +++ b/src/app/config/setup/welcome/welcome.component.scss @@ -0,0 +1,10 @@ +.welcome-screen { + &__heading { + display: block; + text-align: center; + font-size: 6vw; + margin-top: 2vh; + margin-bottom: 6vh; + font-weight: 500; + } +} diff --git a/src/app/config/setup/welcome/welcome.component.ts b/src/app/config/setup/welcome/welcome.component.ts new file mode 100644 index 000000000..66a08117d --- /dev/null +++ b/src/app/config/setup/welcome/welcome.component.ts @@ -0,0 +1,10 @@ +import { Component, Input } from '@angular/core'; + +@Component({ + selector: 'app-config-setup-welcome', + templateUrl: './welcome.component.html', + styleUrls: ['./welcome.component.scss', '../setup.component.scss'], +}) +export class WelcomeComponent { + @Input() update: boolean; +} diff --git a/src/app/control/control.component.scss b/src/app/control/control.component.scss index 9eea167b8..18bdf2b35 100755 --- a/src/app/control/control.component.scss +++ b/src/app/control/control.component.scss @@ -164,6 +164,11 @@ width: 30vw; display: block; float: right; + overflow: scroll; + + &::-webkit-scrollbar { + display: none; + } &-action { display: block; diff --git a/src/app/control/control.component.ts b/src/app/control/control.component.ts index 846bf0b5d..c0dddb508 100644 --- a/src/app/control/control.component.ts +++ b/src/app/control/control.component.ts @@ -3,8 +3,8 @@ import { SafeResourceUrl } from '@angular/platform-browser'; import { Router } from '@angular/router'; import { ConfigService } from '../config/config.service'; -import { OctoprintPrinterProfileAPI } from '../octoprint-api/printerProfileAPI'; import { OctoprintService } from '../octoprint.service'; +import { OctoprintPrinterProfile } from '../octoprint/model/printerProfile'; import { EnclosureService } from '../plugin-service/enclosure.service'; import { PsuControlService } from '../plugin-service/psu-control.service'; import { PrinterService } from '../printer.service'; @@ -16,7 +16,7 @@ import { PrinterProfileService } from '../printerprofile.service'; styleUrls: ['./control.component.scss'], }) export class ControlComponent { - public printerProfile: OctoprintPrinterProfileAPI; + public printerProfile: OctoprintPrinterProfile; public jogDistance = 10; public customActions = []; @@ -36,6 +36,7 @@ export class ControlComponent { this.printerProfile = { name: '_default', model: 'unknown', + current: true, axes: { x: { inverted: false }, y: { inverted: false }, diff --git a/src/app/files.service.ts b/src/app/files.service.ts index 48ba38501..b8cdb2d41 100644 --- a/src/app/files.service.ts +++ b/src/app/files.service.ts @@ -6,7 +6,7 @@ import { Subscription } from 'rxjs'; import { AppService } from './app.service'; import { ConfigService } from './config/config.service'; import { NotificationService } from './notification/notification.service'; -import { OctoprintFilesAPI, OctoprintFolderAPI, OctoprintFolderContentAPI } from './octoprint-api/filesAPI'; +import { OctoprintFile, OctoprintFolder, OctoprintFolderContent } from './octoprint/model/file'; @Injectable({ providedIn: 'root', @@ -38,7 +38,7 @@ export class FilesService { this.httpGETRequest = this.http .get(this.configService.getURL('files' + folderPath), this.configService.getHTTPHeaders()) .subscribe( - (data: OctoprintFolderAPI & OctoprintFolderContentAPI): void => { + (data: OctoprintFolder & OctoprintFolderContent): void => { if ('children' in data) { data.files = data.children; delete data.children; @@ -144,7 +144,7 @@ export class FilesService { this.httpGETRequest = this.http .get(this.configService.getURL('files' + filePath), this.configService.getHTTPHeaders()) .subscribe( - (data: OctoprintFilesAPI): void => { + (data: OctoprintFile): void => { let filamentLength = 0; if (data.gcodeAnalysis) { _.forEach(data.gcodeAnalysis.filament, (tool): void => { @@ -190,7 +190,7 @@ export class FilesService { this.httpGETRequest = this.http .get(this.configService.getURL('files' + filePath), this.configService.getHTTPHeaders()) .subscribe( - (data: OctoprintFilesAPI): void => { + (data: OctoprintFile): void => { const thumbnail = data.thumbnail ? this.configService.getURL(data.thumbnail).replace('/api/', '/') : 'assets/object.svg'; diff --git a/src/app/job.service.ts b/src/app/job.service.ts index 78a8cf81e..1db006730 100644 --- a/src/app/job.service.ts +++ b/src/app/job.service.ts @@ -7,7 +7,7 @@ import { AppService } from './app.service'; import { ConfigService } from './config/config.service'; import { FilesService } from './files.service'; import { NotificationService } from './notification/notification.service'; -import { JobCommand, OctoprintFilament, OctoprintJobAPI } from './octoprint-api/jobAPI'; +import { JobCommand, OctoprintFilament, OctoprintJobStatus } from './octoprint/model/job'; @Injectable({ providedIn: 'root', @@ -37,7 +37,7 @@ export class JobService { this.httpGETRequest = this.http .get(this.configService.getURL('job'), this.configService.getHTTPHeaders()) .subscribe( - async (data: OctoprintJobAPI): Promise => { + async (data: OctoprintJobStatus): Promise => { let job: Job = null; if (data.job && data.job.file.name) { this.printing = ['Printing', 'Pausing', 'Paused', 'Cancelling', 'Printing from SD'].includes( diff --git a/src/app/notification/notification.component.ts b/src/app/notification/notification.component.ts index 0db532857..d06385ec0 100644 --- a/src/app/notification/notification.component.ts +++ b/src/app/notification/notification.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectorRef, Component, OnDestroy } from '@angular/core'; +import { Component, NgZone, OnDestroy } from '@angular/core'; import { Subscription } from 'rxjs'; import { Notification, NotificationService } from './notification.service'; @@ -19,7 +19,7 @@ export class NotificationComponent implements OnDestroy { }; public show = false; - public constructor(private notificationService: NotificationService, private changeDetector: ChangeDetectorRef) { + public constructor(private notificationService: NotificationService, private zone: NgZone) { this.subscriptions.add( this.notificationService .getObservable() @@ -35,13 +35,14 @@ export class NotificationComponent implements OnDestroy { } private setNotification(notification: Notification | 'close'): void { - if (notification === 'close') { - this.hideNotification(); - } else { - this.notification = notification; - this.show = true; - } - this.changeDetector.detectChanges(); + this.zone.run(() => { + if (notification === 'close') { + this.hideNotification(); + } else { + this.notification = notification; + this.show = true; + } + }); } public ngOnDestroy(): void { diff --git a/src/app/octoprint-script.service.ts b/src/app/octoprint-script.service.ts deleted file mode 100644 index eeccc5e97..000000000 --- a/src/app/octoprint-script.service.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { Injectable } from '@angular/core'; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -declare const OctoPrint: any; - -@Injectable() -export class OctoprintScriptService { - public loaded = false; - private checkInterval = 1000; - private numberDownloadFailed = 0; - - async initialize(octoprintURL: string, apiKey: string): Promise { - return new Promise(resolve => { - this.tryDownload(octoprintURL, apiKey, resolve); - }); - } - - tryDownload(octoprintURL: string, apiKey: string, resolve: () => void): void { - this.downloadScript(octoprintURL) - .then(() => { - this.authenticate(apiKey); - console.clear(); - resolve(); - }) - .catch(() => { - if (this.numberDownloadFailed < 30) { - this.numberDownloadFailed++; - } else if (this.numberDownloadFailed === 30) { - this.checkInterval = 15000; - } - setTimeout(this.tryDownload.bind(this), this.checkInterval, octoprintURL, apiKey, resolve); - }); - } - - downloadScript(octoprintURL: string): Promise { - return new Promise((resolve, reject) => { - const octoprintStaticURL = octoprintURL.replace('/api/', '/static/'); - this.loadScript(`${octoprintStaticURL}webassets/packed_client.js`) - .then(() => { - OctoPrint.options.baseurl = octoprintURL; - resolve(); - }) - .catch(() => reject()); - }); - } - - private loadScript(src: string): Promise { - return new Promise((resolve, reject) => { - const script = document.createElement('script'); - script.type = 'text/javascript'; - script.src = src; - script.onload = () => resolve(); - script.onerror = () => reject(); - document.getElementsByTagName('head')[0].appendChild(script); - }); - } - - public authenticate(accessToken: string): void { - OctoPrint.options.apikey = accessToken; - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - public getInstance(): any { - return OctoPrint; - } -} diff --git a/src/app/octoprint/auth.service.ts b/src/app/octoprint/auth.service.ts new file mode 100644 index 000000000..41ffcc1fa --- /dev/null +++ b/src/app/octoprint/auth.service.ts @@ -0,0 +1,31 @@ +/* eslint-disable camelcase */ +import { HttpClient, HttpResponseBase } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; + +@Injectable({ + providedIn: 'root', +}) +export class AuthService { + constructor(private http: HttpClient) {} + + public probeSupport(octoprintURL: string): Observable { + return this.http.get(`${octoprintURL}plugin/appkeys/probe`, { observe: 'response' }); + } + + public startProcess(octoprintURL: string): Observable { + return this.http.post(`${octoprintURL}plugin/appkeys/request`, { app: 'OctoDash' }); + } + + public pollStatus(octoprintURL: string, token: string): Observable { + return this.http.get(`${octoprintURL}plugin/appkeys/request/${token}`, { observe: 'response' }); + } +} + +export interface AppToken { + app_token: string; +} + +export interface TokenSuccess { + api_key: string; +} diff --git a/src/app/octoprint-api/connectionAPI.ts b/src/app/octoprint/model/connection.ts similarity index 57% rename from src/app/octoprint-api/connectionAPI.ts rename to src/app/octoprint/model/connection.ts index 604055a57..e8474547d 100644 --- a/src/app/octoprint-api/connectionAPI.ts +++ b/src/app/octoprint/model/connection.ts @@ -1,16 +1,16 @@ -export interface OctoprintConnectionAPI { - current: OctoprintConnectionCurrentAPI; - options: OctoprintConnectionOptionsAPI; +export interface OctoprintConnection { + current: OctoprintCurrentConnection; + options: OctoprintConnectionOptions; } -interface OctoprintConnectionCurrentAPI { +interface OctoprintCurrentConnection { state: string; port: string; baudrate: number; printerProfile: string; } -interface OctoprintConnectionOptionsAPI { +interface OctoprintConnectionOptions { ports: string[]; baudrates: number[]; printerProfiles: Record; diff --git a/src/app/octoprint-api/filesAPI.ts b/src/app/octoprint/model/file.ts similarity index 81% rename from src/app/octoprint-api/filesAPI.ts rename to src/app/octoprint/model/file.ts index 6a6919100..40325eb10 100644 --- a/src/app/octoprint-api/filesAPI.ts +++ b/src/app/octoprint/model/file.ts @@ -1,6 +1,6 @@ -import { OctoprintFilament } from './jobAPI'; +import { OctoprintFilament } from './job'; -export interface OctoprintFilesAPI { +export interface OctoprintFile { date: number; display: string; gcodeAnalysis?: OctoprintGCodeAnalysis; @@ -17,14 +17,14 @@ export interface OctoprintFilesAPI { thumbnail?: string; } -export interface OctoprintFolderAPI { - files: [OctoprintFilesAPI & OctoprintFolderContentAPI]; +export interface OctoprintFolder { + files: [OctoprintFile & OctoprintFolderContent]; free: number; total: number; } -export interface OctoprintFolderContentAPI { - children: [OctoprintFilesAPI & OctoprintFolderContentAPI]; +export interface OctoprintFolderContent { + children: [OctoprintFile & OctoprintFolderContent]; display: string; name: string; origin: string; diff --git a/src/app/octoprint-api/jobAPI.ts b/src/app/octoprint/model/job.ts similarity index 95% rename from src/app/octoprint-api/jobAPI.ts rename to src/app/octoprint/model/job.ts index 6a41af232..c95212f89 100644 --- a/src/app/octoprint-api/jobAPI.ts +++ b/src/app/octoprint/model/job.ts @@ -1,4 +1,4 @@ -export interface OctoprintJobAPI { +export interface OctoprintJobStatus { job: OctoprintJob; progress: OctoprintProgress; state: string; diff --git a/src/app/octoprint-api/layerProgressAPI.ts b/src/app/octoprint/model/layerProgress.ts similarity index 85% rename from src/app/octoprint-api/layerProgressAPI.ts rename to src/app/octoprint/model/layerProgress.ts index 52dc584d7..6cb1eec72 100644 --- a/src/app/octoprint-api/layerProgressAPI.ts +++ b/src/app/octoprint/model/layerProgress.ts @@ -1,4 +1,4 @@ -export interface OctoprintLayerProgressAPI { +export interface OctoprintLayerProgress { layer: OctoprintLayer; height: OctoprintHeight; fanSpeed: string; diff --git a/src/app/octoprint-api/printerProfileAPI.ts b/src/app/octoprint/model/printerProfile.ts similarity index 59% rename from src/app/octoprint-api/printerProfileAPI.ts rename to src/app/octoprint/model/printerProfile.ts index db5188f02..fde49ab92 100644 --- a/src/app/octoprint-api/printerProfileAPI.ts +++ b/src/app/octoprint/model/printerProfile.ts @@ -1,4 +1,11 @@ -export interface OctoprintPrinterProfileAPI { +export interface OctoprintPrinterProfiles { + profiles: { + [key: string]: OctoprintPrinterProfile; + }; +} + +export interface OctoprintPrinterProfile { + current: boolean; name: string; model: string; axes: OctoprintPrinterAxis; diff --git a/src/app/octoprint-api/printerStatusAPI.ts b/src/app/octoprint/model/printerStatus.ts similarity index 95% rename from src/app/octoprint-api/printerStatusAPI.ts rename to src/app/octoprint/model/printerStatus.ts index baaab294f..11f8971a3 100644 --- a/src/app/octoprint-api/printerStatusAPI.ts +++ b/src/app/octoprint/model/printerStatus.ts @@ -1,4 +1,4 @@ -export interface OctoprintPrinterStatusAPI { +export interface OctoprintPrinterStatus { temperature: OctoprintTemperature; sd: OctoprintSD; state: OctoprintPrinterState; diff --git a/src/app/plugin-service/layer-progress.service.ts b/src/app/plugin-service/layer-progress.service.ts index 76c580df7..49a190b31 100644 --- a/src/app/plugin-service/layer-progress.service.ts +++ b/src/app/plugin-service/layer-progress.service.ts @@ -3,9 +3,9 @@ import { Injectable } from '@angular/core'; import { Observable, Observer, Subscription, timer } from 'rxjs'; import { shareReplay } from 'rxjs/operators'; +import { OctoprintLayerProgress } from '.././octoprint/model/layerProgress'; import { ConfigService } from '../config/config.service'; import { NotificationService } from '../notification/notification.service'; -import { OctoprintLayerProgressAPI } from '../octoprint-api/layerProgressAPI'; @Injectable({ providedIn: 'root', @@ -30,7 +30,7 @@ export class LayerProgressService { this.configService.getHTTPHeaders(), ) .subscribe( - (data: OctoprintLayerProgressAPI): void => { + (data: OctoprintLayerProgress): void => { observer.next({ current: data.layer.current === '-' ? 0 : Number(data.layer.current), total: data.layer.total === '-' ? 0 : Number(data.layer.total), diff --git a/src/app/plugin-service/psu-control.service.ts b/src/app/plugin-service/psu-control.service.ts index 476548d72..a6d1b3be0 100644 --- a/src/app/plugin-service/psu-control.service.ts +++ b/src/app/plugin-service/psu-control.service.ts @@ -4,7 +4,7 @@ import { Subscription } from 'rxjs'; import { ConfigService } from '../config/config.service'; import { NotificationService } from '../notification/notification.service'; -import { JobCommand } from '../octoprint-api/jobAPI'; +import { JobCommand } from '../octoprint/model/job'; @Injectable({ providedIn: 'root', diff --git a/src/app/printer.service.ts b/src/app/printer.service.ts index dceb8576d..8076fce07 100644 --- a/src/app/printer.service.ts +++ b/src/app/printer.service.ts @@ -6,8 +6,8 @@ import { shareReplay } from 'rxjs/operators'; import { ConfigService } from './config/config.service'; import { NotificationService } from './notification/notification.service'; -import { OctoprintConnectionAPI } from './octoprint-api/connectionAPI'; -import { OctoprintPrinterStatusAPI } from './octoprint-api/printerStatusAPI'; +import { OctoprintConnection } from './octoprint/model/connection'; +import { OctoprintPrinterStatus } from './octoprint/model/printerStatus'; @Injectable({ providedIn: 'root', @@ -31,7 +31,7 @@ export class PrinterService { this.httpGETRequest = this.http .get(this.configService.getURL('printer'), this.configService.getHTTPHeaders()) .subscribe( - (data: OctoprintPrinterStatusAPI): void => { + (data: OctoprintPrinterStatus): void => { const printerStatus: PrinterStatusAPI = { status: data.state.text.toLowerCase(), nozzle: { @@ -243,7 +243,7 @@ export class PrinterService { public isPrinterOffline(): Promise { return new Promise((resolve): void => { this.http.get(this.configService.getURL('connection'), this.configService.getHTTPHeaders()).subscribe( - (data: OctoprintConnectionAPI): void => { + (data: OctoprintConnection): void => { resolve(data.current.state === 'Closed' || data.current.state.includes('Error:')); }, (error: HttpErrorResponse): void => { diff --git a/src/app/printerprofile.service.ts b/src/app/printerprofile.service.ts index 30563212a..7e816a807 100644 --- a/src/app/printerprofile.service.ts +++ b/src/app/printerprofile.service.ts @@ -1,11 +1,12 @@ -import { HttpClient, HttpErrorResponse } from '@angular/common/http'; +import { HttpClient, HttpErrorResponse, HttpHeaders } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Router } from '@angular/router'; -import { Subscription } from 'rxjs'; +import { Observable, Subscription } from 'rxjs'; +import { map } from 'rxjs/operators'; import { ConfigService } from './config/config.service'; import { NotificationService } from './notification/notification.service'; -import { OctoprintPrinterProfileAPI } from './octoprint-api/printerProfileAPI'; +import { OctoprintPrinterProfile, OctoprintPrinterProfiles } from './octoprint/model/printerProfile'; import { PrinterService } from './printer.service'; @Injectable({ @@ -22,7 +23,7 @@ export class PrinterProfileService { private router: Router, ) {} - public getDefaultPrinterProfile(): Promise { + public getDefaultPrinterProfile(): Promise { return new Promise((resolve, reject): void => { if (this.httpGETRequest) { this.httpGETRequest.unsubscribe(); @@ -30,7 +31,7 @@ export class PrinterProfileService { this.httpGETRequest = this.http .get(this.configService.getURL('printerprofiles/_default'), this.configService.getHTTPHeaders()) .subscribe( - (printerProfile: OctoprintPrinterProfileAPI): void => { + (printerProfile: OctoprintPrinterProfile): void => { resolve(printerProfile); }, (error: HttpErrorResponse): void => { @@ -53,4 +54,21 @@ export class PrinterProfileService { ); }); } + + // Needed for initial setup. Config not initialized yet, thus values need to be passed manually. + public getActivePrinterProfileName(octoprintURL: string, apiKey: string): Observable { + return this.http + .get(`${octoprintURL}printerprofiles`, { + headers: new HttpHeaders({ + 'x-api-key': apiKey, + }), + }) + .pipe( + map(profiles => { + for (const [_, profile] of Object.entries(profiles.profiles)) { + return profile.name; + } + }), + ); + } } diff --git a/src/app/settings/settings.component.html b/src/app/settings/settings.component.html index ace915d08..d80f0fc73 100644 --- a/src/app/settings/settings.component.html +++ b/src/app/settings/settings.component.html @@ -53,7 +53,7 @@ class="settings__input" name="octoprintURLName" style="width: 23.2vw" - [(ngModel)]="this.config.octoprint.urlSplit.url" + [(ngModel)]="this.config.octoprint.urlSplit.host" required />: { + (data: OctoprintConnection): void => { if (data.current.state === 'Closed') { if (this.connectionRetries <= 0) { this.connectionRetries = 3; diff --git a/src/app/update/update.component.ts b/src/app/update/update.component.ts index 746d8ae46..3e4801c61 100644 --- a/src/app/update/update.component.ts +++ b/src/app/update/update.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectorRef, Component, EventEmitter, OnInit, Output } from '@angular/core'; +import { Component, EventEmitter, NgZone, OnInit, Output } from '@angular/core'; import { ElectronService } from 'ngx-electron'; import { AppService } from '../app.service'; @@ -30,7 +30,7 @@ export class UpdateComponent implements OnInit { public service: AppService, private notificationService: NotificationService, private octoprintService: OctoprintService, - private changeDetector: ChangeDetectorRef, + private zone: NgZone, private electronService: ElectronService, ) {} @@ -56,26 +56,29 @@ export class UpdateComponent implements OnInit { this.electronService.ipcRenderer.on( 'updateDownloadProgress', (_, updateDownloadProgress: UpdateDownloadProgress): void => { - this.updateProgress = updateDownloadProgress; - this.changeDetector.detectChanges(); + this.zone.run(() => { + this.updateProgress = updateDownloadProgress; + }); }, ); this.electronService.ipcRenderer.on('updateDownloadFinished', (): void => { - this.page = 2; - this.changeDetector.detectChanges(); - setTimeout(() => { - const updateProgressBar = document.getElementById('installUpdateProgress'); - updateProgressBar.style.marginLeft = '40vw'; - this.installationAnimationInterval = setInterval(() => { - updateProgressBar.style.marginLeft = updateProgressBar.style.marginLeft === '0vw' ? '40vw' : '0vw'; - }, 2050); - }, 250); + this.zone.run(() => { + this.page = 2; + setTimeout(() => { + const updateProgressBar = document.getElementById('installUpdateProgress'); + updateProgressBar.style.marginLeft = '40vw'; + this.installationAnimationInterval = setInterval(() => { + updateProgressBar.style.marginLeft = updateProgressBar.style.marginLeft === '0vw' ? '40vw' : '0vw'; + }, 2050); + }, 250); + }); }); this.electronService.ipcRenderer.on('updateInstalled', (): void => { - this.page = 3; - this.changeDetector.detectChanges(); + this.zone.run(() => { + this.page = 3; + }); }); } diff --git a/src/index.html b/src/index.html index 3c1c8de4f..7d0b73096 100644 --- a/src/index.html +++ b/src/index.html @@ -12,10 +12,6 @@ rel="stylesheet" /> -