diff --git a/sample.config.json b/sample.config.json
index 218cef434..300330e35 100644
--- a/sample.config.json
+++ b/sample.config.json
@@ -1,11 +1,11 @@
{
"config": {
"octoprint": {
- "accessToken": "INSERT API KEY HERE",
+ "accessToken": "",
"url": "http://localhost:5000/api/"
},
"printer": {
- "name": "NAME HERE",
+ "name": "",
"xySpeed": 100,
"zSpeed": 5,
"defaultTemperatureFanSpeed": {
@@ -18,7 +18,9 @@
"density": 0,
"thickness": 0,
"feedLength": 470,
- "feedSpeed": 100
+ "feedSpeed": 30,
+ "feedSpeedSlow": 3,
+ "purgeDistance": 30
},
"plugins": {
"displayLayerProgress": {
diff --git a/src/app/app.service.ts b/src/app/app.service.ts
index dc21ed07a..78ef57901 100644
--- a/src/app/app.service.ts
+++ b/src/app/app.service.ts
@@ -34,25 +34,27 @@ export class AppService {
}
}
- this.updateError = [".printer should have required property 'defaultTemperatureFanSpeed'"];
+ this.updateError = [
+ ".filament should have required property 'feedSpeedSlow'",
+ ".filament should hsave required property 'purgeDistance'",
+ ];
}
// If the errors can be automatically fixed return true here
public autoFixError(): boolean {
let config = this.configService.getCurrentConfig();
- config.printer.defaultTemperatureFanSpeed = {
- hotend: 200,
- heatbed: 60,
- fan: 100,
- };
+ config.filament.feedLength = 0;
+ config.filament.feedSpeed = 30;
+ config.filament.feedSpeedSlow = 5;
+ config.filament.purgeDistance = 30;
this.configService.saveConfig(config);
this.configService.updateConfig();
- return true;
+ return false;
}
private checkUpdate(): void {
this.http.get('https://api.github.com/repos/UnchartedBull/OctoDash/releases/latest').subscribe(
- (data: GitHubRealeaseInformation): void => {
+ (data: GitHubReleaseInformation): void => {
if (this.version !== data.name.replace('v', '')) {
this.notificationService.setUpdate(
"It's time for an update",
@@ -132,7 +134,7 @@ interface VersionInformation {
version: string;
}
-interface GitHubRealeaseInformation {
+interface GitHubReleaseInformation {
name: string;
[key: string]: string;
}
diff --git a/src/app/config/config.service.ts b/src/app/config/config.service.ts
index f3bf158af..bc86c7dd3 100644
--- a/src/app/config/config.service.ts
+++ b/src/app/config/config.service.ts
@@ -236,6 +236,26 @@ export class ConfigService {
public isPreheatPluginEnabled(): boolean {
return this.config.plugins.preheatButton.enabled;
}
+
+ public isFilamentManagerEnabled(): boolean {
+ return this.config.plugins.filamentManager.enabled;
+ }
+
+ public getFeedLength(): number {
+ return this.config.filament.feedLength;
+ }
+
+ public getFeedSpeed(): number {
+ return this.config.filament.feedSpeed;
+ }
+
+ public getFeedSpeedSlow(): number {
+ return this.config.filament.feedSpeedSlow;
+ }
+
+ public getPurgeDistance(): number {
+ return this.config.filament.purgeDistance;
+ }
}
export interface Config {
@@ -273,6 +293,8 @@ interface Filament {
density: number;
feedLength: number;
feedSpeed: number;
+ feedSpeedSlow: number;
+ purgeDistance: number;
}
interface Plugins {
@@ -385,7 +407,7 @@ const schema = {
filament: {
$id: '#/properties/filament',
type: 'object',
- required: ['density', 'thickness', 'feedLength', 'feedSpeed'],
+ required: ['density', 'thickness', 'feedLength', 'feedSpeed', 'feedSpeedSlow', 'purgeDistance'],
properties: {
density: {
$id: '#/properties/filament/properties/density',
@@ -403,6 +425,14 @@ const schema = {
$id: '#/properties/filament/properties/feedSpeed',
type: 'integer',
},
+ feedSpeedSlow: {
+ $id: '#/properties/filament/properties/feedSpeedSlow',
+ type: 'integer',
+ },
+ purgeDistance: {
+ $id: '#/properties/filament/properties/purgeDistance',
+ type: 'integer',
+ },
},
},
plugins: {
diff --git a/src/app/config/no-config/no-config.component.html b/src/app/config/no-config/no-config.component.html
index 912bf4368..d5dbbdb07 100644
--- a/src/app/config/no-config/no-config.component.html
+++ b/src/app/config/no-config/no-config.component.html
@@ -19,40 +19,55 @@
Hey there!
- It looks like this is the first time starting OctoDash!
- I'll help you setting up your config. If you encounter any issues please create an issue in
- GitHub.
- Thanks for choosing OctoDash :)
+ 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 :)
+ Thanks for choosing OctoDash
- First, tell me some facts about your printer and filament so I can personalize OctoDash.
+ First, 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.
+
-
Filament
-
Diameter:
-
+
+
+
+ I also need some information about your extruder.
+
+
-
+
Now I need to know something about your OctoPrint setup, so I can talk to your printer.
@@ -70,7 +85,7 @@
name="accessToken" style="width: 67vw" required>
-
+
And now personalize me to your liking.
@@ -85,14 +100,13 @@
ms
-
- If you're unsure about what values to use, just use the default. You can change all values (and even
- more stuff, like defining custom actions) later in the settings menu.
+
+ 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?
@@ -133,39 +147,39 @@
PSU Control
-
- Enclosure and PSU Control Plugin can be configured further in the settings!
+
+ Enclosure and PSU Control Plugin can be configured further in the settings.
-
+
Great! I'll check everything.
-
-
-
-
+
+
+
+
Config Validation
-
{{ error }}
+
{{ error }}
-
done
diff --git a/src/app/config/no-config/no-config.component.scss b/src/app/config/no-config/no-config.component.scss
index 73b5e52f3..8afc5fe1c 100644
--- a/src/app/config/no-config/no-config.component.scss
+++ b/src/app/config/no-config/no-config.component.scss
@@ -5,6 +5,11 @@
margin-left: 2vh;
margin-right: 3vh;
font-size: 3.6vw;
+
+ fa-icon {
+ display: block;
+ margin-top: 2vh;
+ }
}
&__progress-bar {
@@ -12,7 +17,7 @@
border-radius: 2vh;
background-color: #44bd32;
width: 0;
- transition: width .7s ease-in-out;
+ transition: width 0.7s ease-in-out;
&-wrapper {
background-color: #718093;
@@ -56,7 +61,7 @@
&-prefix {
font-size: 3vw;
- opacity: .7;
+ opacity: 0.7;
padding-right: 1vw;
}
}
@@ -83,16 +88,16 @@
}
&-checked {
- background-color: #2196F3;
+ background-color: #2196f3;
width: 2.4vw;
height: 2.4vw;
display: block;
- margin-left: .75vw;
- margin-top: .75vw;
- border-radius: .7vw;
+ margin-left: 0.75vw;
+ margin-top: 0.75vw;
+ border-radius: 0.7vw;
&-disabled {
- background-color: rgba(255, 255, 255, .5)
+ background-color: rgba(255, 255, 255, 0.5);
}
}
}
@@ -103,6 +108,15 @@
text-align: center;
}
+ &-explanation {
+ font-size: 3vw;
+ font-style: italic;
+ opacity: 0.7;
+ margin-top: 4vh;
+ padding: 2vh 4vw;
+ text-align: center;
+ }
+
&__1 {
&-welcome {
display: block;
@@ -115,27 +129,17 @@
}
&__2 {
- &-filament-divider {
- display: block;
+ &-form {
text-align: left;
- margin-left: 4.3vw;
- margin-top: 7vh;
- margin-bottom: -7vh;
- font-weight: 500;
+ margin-left: 4vw;
}
- }
- &__3 {
- &-explanation {
- font-size: 3vw;
- font-style: italic;
- opacity: .7;
- margin-top: 4vh;
- padding: 2vh 4vw;
+ &-input {
+ margin-top: 5vh;
}
}
- &__5 {
+ &__6 {
&-check-wrapper {
width: 100%;
text-align: center;
diff --git a/src/app/config/no-config/no-config.component.ts b/src/app/config/no-config/no-config.component.ts
index cf129b5df..6c92ca9b9 100644
--- a/src/app/config/no-config/no-config.component.ts
+++ b/src/app/config/no-config/no-config.component.ts
@@ -11,7 +11,7 @@ import { Config, ConfigService } from '../config.service';
})
export class NoConfigComponent implements OnInit {
public page = 0;
- public totalPages = 5;
+ public totalPages = 6;
private configUpdate: boolean;
public config: Config;
@@ -24,6 +24,7 @@ export class NoConfigComponent implements OnInit {
public constructor(private configService: ConfigService, private http: HttpClient, private router: Router) {
this.configUpdate = this.configService.isUpdate();
+ console.log(this.configUpdate);
if (this.configUpdate) {
this.config = configService.getCurrentConfig();
} else {
@@ -45,8 +46,10 @@ export class NoConfigComponent implements OnInit {
filament: {
thickness: 1.75,
density: 1.25,
- feedLength: 470,
- feedSpeed: 100,
+ feedLength: 0,
+ feedSpeed: 30,
+ feedSpeedSlow: 3,
+ purgeDistance: 30,
},
plugins: {
displayLayerProgress: {
@@ -140,7 +143,7 @@ export class NoConfigComponent implements OnInit {
'x-api-key': this.config.octoprint.accessToken,
}),
};
- this.http.get(this.config.octoprint.url + 'version', httpHeaders).subscribe(
+ this.http.get(this.config.octoprint.url + 'connection', httpHeaders).subscribe(
(): void => {
this.octoprintConnection = true;
this.saveConfig();
@@ -188,7 +191,7 @@ export class NoConfigComponent implements OnInit {
}
public decreasePage(): void {
- if (this.page === 4) {
+ if (this.page === 5) {
this.config = this.configService.revertConfigForInput(this.config);
}
this.page -= 1;
diff --git a/src/app/filament/filament.component.html b/src/app/filament/filament.component.html
index 54e28528c..dd68c16b7 100644
--- a/src/app/filament/filament.component.html
+++ b/src/app/filament/filament.component.html
@@ -1,5 +1,133 @@
-:(
-
- This feature has not been implemented yet.
- If you really, really want it before anything else - go to GitHub and like issue #14.
-back
+
+
+
+ back
+
+
+
+
+
+
+ skip
+
+
+
+
+
+
select your new filament
+
+
+
+ {{ spool.profile.material }}
+
+
+ {{ spool.displayName }}
+
+ {{ getSpoolWeightLeft(spool.weight, spool.used) }}g left
+
+
+
+ no filament spools found
+
+
+ loading spools ...
+
+
+
+
+
heating the nozzle
+
+
+
+
+ {{ hotendTarget }}
+ °C
+ /{{ hotendTemperature }}
+ °C
+
+
+
+
+
+
+
wait {{ automaticHeatingStartSeconds }} s or -
+
start
+
+
+
+
unloading filament
+
+
{{ getFeedSpeed() }} mm/s
+
+
+
+
load new filament
+
Only put a little filament in, I'll pull in the rest.
+
+
+
+
{{ selectedSpool.displayName }}
+
+
+ I'll wait for you.
+
+
+
+
+
loading filament
+
+
{{ getFeedSpeed() }} mm/s
+
+
+
+
purging filament
+
{{ purgeAmount }}mm
+
+
+
diff --git a/src/app/filament/filament.component.scss b/src/app/filament/filament.component.scss
index e69de29bb..f22c770a4 100644
--- a/src/app/filament/filament.component.scss
+++ b/src/app/filament/filament.component.scss
@@ -0,0 +1,311 @@
+.filament {
+ padding: 2vh 2vw 0;
+
+ &__progress-bar {
+ height: 4vh;
+ border-radius: 2vh;
+ background-color: #44bd32;
+ width: 0;
+ transition: width 0.7s ease-in-out;
+
+ &-wrapper {
+ background-color: #718093;
+ height: 4vh;
+ width: 20vw;
+ display: inline-block;
+ border-radius: 2vh;
+
+ &-wide {
+ width: 50vw;
+ margin: 14vh auto 3vh;
+ display: block;
+ height: 7vh;
+ background-color: transparent;
+ border: 3px solid #f5f6fa;
+ border-radius: 3vh;
+
+ > div {
+ height: 7vh;
+ width: 50vw;
+ background-color: transparent;
+ }
+ }
+ }
+ }
+
+ &-heading {
+ display: block;
+ text-align: center;
+ font-size: 1rem;
+ }
+
+ &-filaments {
+ margin-top: 2vh;
+ width: 94vw;
+ display: block;
+ height: 67vh;
+ overflow-y: scroll;
+ -webkit-overflow-scrolling: touch;
+ padding-right: 1.5vw;
+
+ &::before {
+ content: '';
+ width: 92vw;
+ height: 69.2vh;
+ position: fixed;
+ left: 1vw;
+ top: 31.5vh;
+ z-index: 2;
+ pointer-events: none;
+ background: linear-gradient(#353b48, transparent 6%, transparent 94%, #353b48);
+ }
+
+ td {
+ font-size: 3.2vw;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ display: inline-block;
+ line-height: 9vh;
+
+ &:last-of-type {
+ line-height: 7vh;
+ }
+ }
+
+ tr {
+ background-color: #718093;
+ border-radius: 1vw;
+ padding: 0 1vw;
+ display: block;
+ margin-bottom: 2vh;
+ height: 10.5vh;
+
+ &:first-of-type {
+ margin-top: 3.5vh;
+ }
+
+ &:last-of-type {
+ margin-bottom: 3.5vh;
+ }
+ }
+
+ &__type {
+ width: 15vw;
+ text-align: center;
+ margin-left: -2vw;
+
+ &-box {
+ font-size: 2vw;
+ border: 3px solid;
+ width: auto;
+ padding: 1.3vh 1.5vw;
+ border-radius: 0.15rem;
+ font-weight: 500;
+ }
+ }
+
+ &__name {
+ width: 62vw;
+ }
+
+ &__weight-left {
+ width: 14vw;
+ text-align: right;
+ }
+ }
+
+ &-no-filaments {
+ text-align: center;
+ font-size: 0.9rem;
+ margin-top: 20vh;
+ }
+
+ &-heating {
+ &__center {
+ text-align: center;
+ }
+
+ &__information {
+ display: inline-block;
+ width: 27vw;
+ font-size: 0.65rem;
+ text-align: left;
+ margin-left: 7vw;
+ line-height: 1rem;
+ overflow: visible;
+ vertical-align: top;
+ margin-top: 8vh;
+ margin-right: -5vw;
+
+ &-color {
+ width: 5vw;
+ height: 5vw;
+ border-radius: 100%;
+ margin: 0 auto;
+ margin-bottom: 3vh;
+ border: 3px solid #f5f6fa;
+ }
+
+ &-name {
+ text-align: center;
+ font-size: 0.8rem;
+ opacity: 0.7;
+ display: block;
+ }
+
+ &-offset {
+ &-wrapper {
+ text-align: center;
+ margin-top: 6vh;
+ line-height: 2rem;
+ }
+
+ &-indicator {
+ opacity: 0.7;
+ vertical-align: 0.5rem;
+ }
+
+ &-value {
+ font-size: 2.5rem;
+ }
+ }
+ }
+
+ &__controller {
+ display: inline-block;
+ width: 35vw;
+ border: solid 0.4vw;
+ border-radius: 2vw;
+ margin-top: 6vh;
+
+ &-row {
+ display: flex;
+ }
+
+ &-value {
+ padding: 3vh 6vw;
+ font-size: 6vw;
+ font-weight: 500;
+ text-align: center;
+ border-top: solid 0.4vw rgba(255, 255, 255, 0.5);
+ border-bottom: solid 0.4vw rgba(255, 255, 255, 0.5);
+
+ &-unit {
+ text-align: center;
+ font-size: 4vw;
+ font-weight: 400;
+ }
+ }
+
+ &-current {
+ display: block;
+ margin-top: 1vh;
+ font-size: 4vw;
+
+ > span {
+ font-size: 2.8vw;
+ }
+ }
+
+ &-control {
+ padding: 2vh 6vw;
+ font-size: 0.9rem;
+ flex: 1;
+
+ &:first-of-type {
+ border-right: solid 0.4vw rgba(255, 255, 255, 0.5);
+ }
+ }
+ }
+
+ &__start {
+ &-wrapper {
+ text-align: center;
+ margin-top: 4.5vh;
+
+ > span {
+ font-size: 50%;
+ display: inline-block;
+ margin-left: -1vw;
+ margin-right: 1vw;
+ vertical-align: 1.2vw;
+
+ > span {
+ font-size: 120%;
+ }
+ }
+ }
+
+ &-heating {
+ display: inline-block;
+ background-color: #44bd32;
+ padding: 1vh 2vw;
+ border-radius: 1vw;
+ font-size: 0.8rem;
+ box-shadow: 0 10px 19px -8px rgba(0, 0, 0, 0.75);
+ }
+ }
+ }
+
+ &-move {
+ &__speed {
+ font-size: 60%;
+ opacity: 0.8;
+ font-style: italic;
+ display: block;
+ text-align: center;
+ }
+
+ &__wrapper {
+ text-align: center;
+ position: absolute;
+ width: 96vw;
+ bottom: 5vh;
+ }
+
+ &__cancel {
+ display: inline-block;
+ background-color: #c23616;
+ padding: 2vh 2vw;
+ border-radius: 8px;
+ }
+ }
+
+ &__done {
+ display: inline-block;
+ background-color: #44bd32;
+ padding: 2vh 2vw;
+ border-radius: 8px;
+ }
+
+ &-purge {
+ &__amount {
+ text-align: center;
+ margin-top: 12vh;
+ font-size: 250%;
+ }
+
+ &__more,
+ &__done {
+ display: inline-block;
+ padding: 2vh 2vw;
+ border-radius: 8px;
+ }
+
+ &__more {
+ background-color: #7f8fa6;
+ margin-right: 10vw;
+ }
+
+ &__done {
+ background-color: #44bd32;
+ }
+ }
+}
+
+.scroll__thumb-inactive {
+ right: 2.5vw;
+ top: 36vh;
+ height: 57.2vh;
+}
diff --git a/src/app/filament/filament.component.ts b/src/app/filament/filament.component.ts
index 48d61208c..56f528061 100644
--- a/src/app/filament/filament.component.ts
+++ b/src/app/filament/filament.component.ts
@@ -1,10 +1,313 @@
-import { Component } from '@angular/core';
+import { Component, OnInit } from '@angular/core';
+import { Router } from '@angular/router';
+
+import { ConfigService } from '../config/config.service';
+import { FilamentManagerService, FilamentSpool, FilamentSpoolList } from '../plugin-service/filament-manager.service';
+import { PrinterService, PrinterStatusAPI } from '../printer.service';
@Component({
selector: 'app-filament',
templateUrl: './filament.component.html',
styleUrls: ['./filament.component.scss'],
})
-export class FilamentComponent {
- public constructor() {}
+export class FilamentComponent implements OnInit {
+ private selectedSpool: FilamentSpool;
+ private currentSpool: FilamentSpool;
+ private totalPages = 5;
+
+ public page: number;
+ private timeout: number;
+ private timeout2: number;
+
+ public filamentSpools: FilamentSpoolList;
+ public isLoadingSpools = true;
+
+ public hotendTarget: number;
+ public hotendTemperature: number;
+ public automaticHeatingStartSeconds: number;
+ public isHeating: boolean;
+
+ private feedSpeedSlow = false;
+
+ public purgeAmount: number;
+
+ public constructor(
+ private router: Router,
+ private configService: ConfigService,
+ private filamentManagerService: FilamentManagerService,
+ private printerService: PrinterService,
+ ) {}
+
+ public ngOnInit(): void {
+ if (this.configService.isFilamentManagerEnabled()) {
+ this.setPage(0);
+ } else {
+ this.setPage(1);
+ }
+ this.hotendTarget = this.configService.getDefaultHotendTemperature();
+ this.automaticHeatingStartSeconds = 6;
+ this.printerService.getObservable().subscribe((printerStatus: PrinterStatusAPI): void => {
+ this.hotendTemperature = printerStatus.nozzle.current;
+ });
+ }
+
+ public increasePage(): void {
+ if (this.page < this.totalPages) {
+ if (
+ (this.page === 1 && this.configService.getFeedLength() === 0) ||
+ (this.page === 3 && this.configService.getFeedLength() === 0)
+ ) {
+ this.setPage(this.page + 2);
+ } else {
+ this.setPage(this.page + 1);
+ }
+ } else if (this.page === this.totalPages) {
+ this.router.navigate(['/main-screen']);
+ }
+ }
+
+ // PAGINATION
+
+ public decreasePage(): void {
+ if (this.page === 0) {
+ this.router.navigate(['/main-screen']);
+ } else if (this.page === 1 && this.configService.isFilamentManagerEnabled()) {
+ this.setPage(0);
+ } else if (this.page === 2 || this.page === 3) {
+ this.setPage(1);
+ } else if (this.page === 4 || this.page === 5) {
+ this.setPage(3);
+ }
+ }
+
+ private setPage(page: number): void {
+ clearTimeout(this.timeout);
+ clearTimeout(this.timeout2);
+ if (this.page === 4) {
+ this.feedSpeedSlow = false;
+ }
+ if (page === 0) {
+ this.selectedSpool = null;
+ this.getSpools();
+ } else if (page === 1) {
+ this.isHeating = false;
+ this.automaticHeatingStartSeconds = 6;
+ this.automaticHeatingStartTimer();
+ } else if (page === 2) {
+ this.unloadSpool();
+ } else if (page === 3) {
+ this.disableExtruderStepper();
+ } else if (page === 4) {
+ this.loadSpool();
+ } else if (page === 5) {
+ this.purgeAmount = this.configService.getPurgeDistance();
+ this.purgeFilament(this.purgeAmount);
+ }
+ this.page = page;
+ if (this.page > 0) {
+ setTimeout((): void => {
+ document.getElementById('progressBar').style.width = this.page * (20 / this.totalPages) + 'vw';
+ }, 200);
+ }
+ }
+
+ // FILAMENT MANAGEMENT
+
+ private getSpools(): void {
+ this.isLoadingSpools = true;
+ this.filamentManagerService
+ .getSpoolList()
+ .then((spools: FilamentSpoolList): void => {
+ this.filamentSpools = spools;
+ })
+ .catch((): void => {
+ this.filamentSpools = null;
+ })
+ .finally((): void => {
+ this.filamentManagerService
+ .getCurrentSpool()
+ .then((spool: FilamentSpool): void => {
+ this.currentSpool = spool;
+ })
+ .catch((): void => {
+ this.currentSpool = null;
+ })
+ .finally((): void => {
+ this.isLoadingSpools = false;
+ });
+ });
+ }
+
+ public getSpoolWeightLeft(weight: number, used: number): number {
+ return Math.floor(weight - used);
+ }
+
+ public getSpoolTemperatureOffset(): string {
+ return `${
+ this.selectedSpool.temp_offset === 0 ? '±' : this.selectedSpool.temp_offset > 0 ? '+' : '-'
+ }${Math.abs(this.selectedSpool.temp_offset)}`;
+ }
+
+ public getCurrentSpoolColor(): string {
+ if (this.currentSpool) {
+ return this.currentSpool.color;
+ } else {
+ return '#44bd32';
+ }
+ }
+
+ public getSelectedSpoolColor(): string {
+ if (this.selectedSpool) {
+ return this.selectedSpool.color;
+ } else {
+ return '#44bd32';
+ }
+ }
+
+ public getFeedSpeed(): number {
+ if (this.feedSpeedSlow) {
+ return this.configService.getFeedSpeedSlow();
+ } else {
+ return this.configService.getFeedSpeed();
+ }
+ }
+
+ public setSpool(spool: FilamentSpool): void {
+ this.selectedSpool = spool;
+ this.hotendTarget = this.hotendTarget + spool.temp_offset;
+ this.increasePage();
+ }
+
+ private unloadSpool(): void {
+ this.printerService.extrude(this.configService.getFeedLength() * -1, this.configService.getFeedSpeed());
+ setTimeout((): void => {
+ const unloadingProgressBar = document.getElementById('filamentUnloadBar');
+ unloadingProgressBar.style.backgroundColor = this.getCurrentSpoolColor();
+ const unloadTime = this.configService.getFeedLength() / this.configService.getFeedSpeed() + 0.5;
+ unloadingProgressBar.style.transition = 'width ' + unloadTime + 's ease-in';
+ setTimeout((): void => {
+ unloadingProgressBar.style.width = '0vw';
+ this.timeout = setTimeout((): void => {
+ this.increasePage();
+ }, unloadTime * 1000 + 500);
+ }, 200);
+ }, 0);
+ }
+
+ private loadSpool(): void {
+ const loadTimeFast = (this.configService.getFeedLength() * 0.8) / this.configService.getFeedSpeed();
+ const loadTimeSlow = (this.configService.getFeedLength() * 0.1) / this.configService.getFeedSpeedSlow();
+ const loadTime = loadTimeFast + loadTimeSlow + 0.5;
+ this.printerService.extrude(this.configService.getFeedLength() * 0.75, this.configService.getFeedSpeed());
+ setTimeout((): void => {
+ const loadingProgressBar = document.getElementById('filamentLoadBar');
+ loadingProgressBar.style.backgroundColor = this.getSelectedSpoolColor();
+
+ loadingProgressBar.style.transition = 'width ' + loadTime + 's ease-in';
+ setTimeout((): void => {
+ loadingProgressBar.style.width = '50vw';
+ this.timeout = setTimeout((): void => {
+ this.printerService.extrude(
+ this.configService.getFeedLength() * 0.17,
+ this.configService.getFeedSpeedSlow(),
+ );
+ this.feedSpeedSlow = true;
+ this.timeout2 = setTimeout((): void => {
+ this.increasePage();
+ }, loadTimeSlow * 1000 + 400);
+ }, loadTimeFast * 1000 + 200);
+ }, 200);
+ }, 0);
+ }
+
+ public setSpoolSelection(): void {
+ this.printerService.setTemperatureHotend(0);
+ if (this.selectedSpool) {
+ this.filamentManagerService.setCurrentSpool(this.selectedSpool).finally(this.increasePage.bind(this));
+ } else {
+ this.increasePage();
+ }
+ }
+
+ public stopExtruderMovement(): void {
+ this.printerService.stopMotors();
+ clearTimeout(this.timeout);
+ clearTimeout(this.timeout2);
+
+ let bar: HTMLElement;
+ const wrapper = (document.getElementsByClassName(
+ 'filament__progress-bar-wrapper-wide',
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ )[0] as any) as HTMLElement;
+
+ if (document.getElementById('filamentLoadBar')) {
+ bar = document.getElementById('filamentLoadBar');
+ } else {
+ bar = document.getElementById('filamentUnloadBar');
+ }
+
+ bar.style.width = Math.floor(bar.getBoundingClientRect().width) + 'px';
+ wrapper.style.borderColor = '#c23616';
+ }
+
+ private disableExtruderStepper(): void {
+ this.printerService.executeGCode('M18 E ');
+ }
+
+ // NOZZLE HEATING
+
+ public changeHotendTarget(value: number): void {
+ this.hotendTarget = this.hotendTarget + value;
+ if (this.hotendTarget < 0) {
+ this.hotendTarget = 0;
+ }
+ if (this.hotendTarget > 999) {
+ this.hotendTarget = 999;
+ }
+ if (!this.isHeating) {
+ this.automaticHeatingStartSeconds = 5;
+ } else {
+ this.setNozzleTemperature();
+ }
+ }
+
+ private automaticHeatingStartTimer(): void {
+ this.automaticHeatingStartSeconds--;
+ if (this.automaticHeatingStartSeconds === 0) {
+ this.setNozzleTemperature();
+ } else {
+ this.timeout = setTimeout(this.automaticHeatingStartTimer.bind(this), 1000);
+ }
+ }
+
+ public setNozzleTemperature(): void {
+ if (this.page === 1) {
+ this.isHeating = true;
+ this.printerService.setTemperatureHotend(this.hotendTarget);
+ if (this.timeout) {
+ clearTimeout(this.timeout);
+ }
+ if (this.timeout2) {
+ clearTimeout(this.timeout2);
+ }
+ this.timeout2 = setTimeout(this.checkTemperature.bind(this), 1500);
+ }
+ }
+
+ private checkTemperature(): void {
+ if (this.hotendTemperature >= this.hotendTarget) {
+ this.increasePage();
+ } else {
+ this.timeout2 = setTimeout(this.checkTemperature.bind(this), 1500);
+ }
+ }
+
+ public increasePurgeAmount(length: number): void {
+ this.purgeAmount += length;
+ this.purgeFilament(length);
+ }
+
+ public purgeFilament(length: number): void {
+ this.printerService.extrude(length, this.configService.getFeedSpeedSlow());
+ }
}
diff --git a/src/app/plugin-service/filament-manager.service.ts b/src/app/plugin-service/filament-manager.service.ts
new file mode 100644
index 000000000..20dd32b94
--- /dev/null
+++ b/src/app/plugin-service/filament-manager.service.ts
@@ -0,0 +1,172 @@
+import { HttpClient, HttpErrorResponse } from '@angular/common/http';
+import { Injectable } from '@angular/core';
+import { Subscription } from 'rxjs';
+
+import { ConfigService } from '../config/config.service';
+import { NotificationService } from '../notification/notification.service';
+
+const colorRegexp = /\((.*)\)$/g;
+
+@Injectable({
+ providedIn: 'root',
+})
+export class FilamentManagerService {
+ private httpGETRequest: Subscription;
+ private httpPOSTRequest: Subscription;
+
+ public constructor(
+ private configService: ConfigService,
+ private notificationService: NotificationService,
+ private http: HttpClient,
+ ) {}
+
+ public getSpoolList(): Promise {
+ return new Promise((resolve, reject): void => {
+ if (this.httpGETRequest) {
+ this.httpGETRequest.unsubscribe();
+ }
+ this.httpGETRequest = this.http
+ .get(
+ this.configService.getURL('plugin/filamentmanager/spools').replace('/api', ''),
+ this.configService.getHTTPHeaders(),
+ )
+ .subscribe(
+ (spools: FilamentSpoolList): void => {
+ spools.spools.forEach((spool): void => {
+ let match = colorRegexp.exec(spool.name);
+ if (match) {
+ spool.color = match[1];
+ spool.displayName = `${spool.profile.vendor} - ${spool.name.replace(match[0], '')}`;
+ } else {
+ spool.color = '#f5f6fa';
+ spool.displayName = `${spool.profile.vendor} - ${spool.name}`;
+ }
+ colorRegexp.lastIndex = 0;
+ });
+ resolve(spools);
+ },
+ (error: HttpErrorResponse): void => {
+ this.notificationService.setError("Can't load filament spools!", error.message);
+ reject();
+ },
+ );
+ });
+ }
+
+ public getCurrentSpool(): Promise {
+ return new Promise((resolve, reject): void => {
+ if (this.httpGETRequest) {
+ this.httpGETRequest.unsubscribe();
+ }
+ this.httpGETRequest = this.http
+ .get(
+ this.configService.getURL('plugin/filamentmanager/selections').replace('/api', ''),
+ this.configService.getHTTPHeaders(),
+ )
+ .subscribe(
+ (selections: FilamentSelections): void => {
+ if (selections.selections.length > 0) {
+ let match = colorRegexp.exec(selections.selections[0].spool.name);
+ if (match) {
+ selections.selections[0].spool.color = match[1];
+ selections.selections[0].spool.displayName = `${
+ selections.selections[0].spool.profile.vendor
+ } - ${selections.selections[0].spool.name.replace(match[0], '')}`;
+ } else {
+ selections.selections[0].spool.color = '#f5f6fa';
+ selections.selections[0].spool.displayName = `${selections.selections[0].spool.profile.vendor} - ${selections.selections[0].spool.name}`;
+ }
+ colorRegexp.lastIndex = 0;
+ resolve(selections.selections[0].spool);
+ }
+ resolve(null);
+ },
+ (error: HttpErrorResponse): void => {
+ this.notificationService.setError("Can't load filament spools!", error.message);
+ reject();
+ },
+ );
+ });
+ }
+
+ public setCurrentSpool(spool: FilamentSpool): Promise {
+ return new Promise((resolve, reject): void => {
+ let setSpoolBody: FilamentSelectionPatch = {
+ selection: {
+ tool: 0,
+ spool: spool,
+ },
+ };
+ this.httpPOSTRequest = this.http
+ .patch(
+ this.configService.getURL('plugin/filamentmanager/selections/0').replace('/api', ''),
+ setSpoolBody,
+ this.configService.getHTTPHeaders(),
+ )
+ .subscribe(
+ (selection: FilamentSelectionConfirm): void => {
+ if (selection.selection.spool.id === spool.id) {
+ resolve();
+ } else {
+ this.notificationService.setError(
+ `Spool IDs didn't match`,
+ `Can't change spool. Please change spool manually in the OctoPrint UI.`,
+ );
+ reject();
+ }
+ },
+ (error: HttpErrorResponse): void => {
+ this.notificationService.setError("Can't set new spool!", error.message);
+ reject();
+ },
+ );
+ });
+ }
+}
+
+export interface FilamentSpoolList {
+ spools: FilamentSpool[];
+}
+
+export interface FilamentSelections {
+ selections: FilamentSelection[];
+}
+
+interface FilamentSelectionPatch {
+ selection: {
+ tool: number;
+ spool: FilamentSpool;
+ };
+}
+
+interface FilamentSelectionConfirm {
+ selection: FilamentSelection;
+}
+
+interface FilamentSelection {
+ // eslint-disable-next-line camelcase
+ client_id: string;
+ spool: FilamentSpool;
+ tool: number;
+}
+
+export interface FilamentSpool {
+ /* eslint-disable camelcase */
+ cost: number;
+ id: number;
+ name: string;
+ displayName?: string;
+ color?: string;
+ profile: FilamentProfile;
+ temp_offset: number;
+ used: number;
+ weight: number;
+}
+
+interface FilamentProfile {
+ density: number;
+ diameter: number;
+ id: number;
+ material: string;
+ vendor: string;
+}
diff --git a/src/app/printer.service.ts b/src/app/printer.service.ts
index 847f5ce27..59b9f99c9 100644
--- a/src/app/printer.service.ts
+++ b/src/app/printer.service.ts
@@ -96,6 +96,10 @@ export class PrinterService {
return this.observable;
}
+ public stopMotors(): void {
+ this.executeGCode('M410');
+ }
+
public jog(x: number, y: number, z: number): void {
const jogPayload: JogCommand = {
command: 'jog',
@@ -114,6 +118,43 @@ export class PrinterService {
);
}
+ public extrude(amount: number, speed: number): void {
+ let multiplier = 1;
+ let toBeExtruded: number;
+ if (amount < 0) {
+ multiplier = -1;
+ toBeExtruded = amount * -1;
+ } else {
+ toBeExtruded = amount;
+ }
+
+ while (toBeExtruded > 0) {
+ if (toBeExtruded >= 100) {
+ toBeExtruded -= 100;
+ this.moveExtruder(100 * multiplier, speed);
+ } else {
+ this.moveExtruder(toBeExtruded * multiplier, speed);
+ toBeExtruded = 0;
+ }
+ }
+ }
+
+ private moveExtruder(amount: number, speed: number): void {
+ const extrudePayload: ExtrudeCommand = {
+ command: 'extrude',
+ amount,
+ speed: speed * 60,
+ };
+ this.httpPOSTRequest = this.http
+ .post(this.configService.getURL('printer/tool'), extrudePayload, this.configService.getHTTPHeaders())
+ .subscribe(
+ (): void => null,
+ (error: HttpErrorResponse): void => {
+ this.notificationService.setError("Can't extrude Filament!", error.message);
+ },
+ );
+ }
+
public executeGCode(gCode: string): void {
if (this.httpPOSTRequest) {
this.httpPOSTRequest.unsubscribe();
@@ -238,13 +279,19 @@ export interface PrinterValue {
}
interface JogCommand {
- command: string;
+ command: 'jog';
x: number;
y: number;
z: number;
speed: number;
}
+interface ExtrudeCommand {
+ command: 'extrude';
+ amount: number;
+ speed: number;
+}
+
interface GCodeCommand {
commands: string[];
}
diff --git a/src/app/settings/settings.component.html b/src/app/settings/settings.component.html
index 29c0307aa..638b4d03c 100644
--- a/src/app/settings/settings.component.html
+++ b/src/app/settings/settings.component.html
@@ -114,7 +114,7 @@
- Bowden Length
+ Feed Length
(mm)
Feed Speed
- (mm/sec)
+ (mm/s)
+
+
+ Feed Speed Slow
+ (mm/s)
+
+
+
+ Please set these values carefully, as they possibly
+ can destroy parts of your printer, just don't set your Feed Speed to high and your Feed
+ Length to long.
diff --git a/src/app/settings/settings.component.scss b/src/app/settings/settings.component.scss
index d8ad46537..9be117af0 100644
--- a/src/app/settings/settings.component.scss
+++ b/src/app/settings/settings.component.scss
@@ -265,6 +265,13 @@
}
}
+.filament-feed-speed-info {
+ font-size: 1.7vw;
+ text-align: justify;
+ opacity: 0.7;
+ font-style: italic;
+}
+
@keyframes fadein {
from {
opacity: 0;