Skip to content

Commit

Permalink
Action commands and printer notifications (#2586)
Browse files Browse the repository at this point in the history
* change setNotification to setInfo conforming to standard logging levels and clarifying notification roles

* implement printernotification socket event emitter

* implement printernotification prompt

* handle prompts correctly

* add reheating message

* update import

* starting cleanup

* update notification styles

* cleanup

* clean up

* fix codefactor issues

* only show notifications with data

* improve notifications

Co-authored-by: Pierre-Alexis Ciavaldini <[email protected]>
  • Loading branch information
UnchartedBull and pciavald committed Feb 22, 2022
1 parent d64fcbd commit 8801423
Show file tree
Hide file tree
Showing 13 changed files with 288 additions and 87 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/manual.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Manual Build with Artifacts

on:
workflow_dispatch:

jobs:
build:
runs-on: ubuntu-latest
Expand Down
11 changes: 9 additions & 2 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,14 +139,21 @@ export function playerFactory(): LottiePlayer {
[
{
provide: SocketService,
deps: [ConfigService, SystemService, ConversionService, HttpClient],
deps: [ConfigService, SystemService, ConversionService, NotificationService, HttpClient],
useFactory: (
configService: ConfigService,
systemService: SystemService,
conversionService: ConversionService,
notificationService: NotificationService,
httpClient: HttpClient,
) => {
return new OctoPrintSocketService(configService, systemService, conversionService, httpClient);
return new OctoPrintSocketService(
configService,
systemService,
conversionService,
notificationService,
httpClient,
);
},
},
],
Expand Down
46 changes: 24 additions & 22 deletions src/app/event.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,30 +18,32 @@ export class EventService implements OnDestroy {
private router: Router,
) {
this.subscriptions.add(
this.socketService.getEventSubscribable().subscribe((event: PrinterEvent) => {
if (event === PrinterEvent.PRINTING || event === PrinterEvent.PAUSED) {
setTimeout(() => {
this.printing = true;
}, 500);
} else {
setTimeout(() => {
this.printing = false;
}, 1000);
}
this.socketService.getEventSubscribable().subscribe((event: PrinterEvent) => this.handlePrinterEvent(event)),
);
}

if (event === PrinterEvent.CLOSED) {
this.router.navigate(['/standby']);
} else if (event === PrinterEvent.CONNECTED) {
setTimeout(() => {
if (this.configService.isTouchscreen()) {
this.router.navigate(['/main-screen']);
} else {
this.router.navigate(['/main-screen-no-touch']);
}
}, 1000);
private handlePrinterEvent(event: PrinterEvent): void {
if (event === PrinterEvent.PRINTING || event === PrinterEvent.PAUSED) {
setTimeout(() => {
this.printing = true;
}, 500);
} else {
setTimeout(() => {
this.printing = false;
}, 1000);
}

if (event === PrinterEvent.CLOSED) {
this.router.navigate(['/standby']);
} else if (event === PrinterEvent.CONNECTED) {
setTimeout(() => {
if (this.configService.isTouchscreen()) {
this.router.navigate(['/main-screen']);
} else {
this.router.navigate(['/main-screen-no-touch']);
}
}),
);
}, 500);
}
}

ngOnDestroy(): void {
Expand Down
7 changes: 7 additions & 0 deletions src/app/model/event.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,10 @@ export enum PrinterEvent {
IDLE,
UNKNOWN,
}

export interface PrinterNotification {
message?: string;
action?: string;
text?: string;
choices?: string[];
}
5 changes: 4 additions & 1 deletion src/app/model/octoprint/socket.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ export interface OctoprintSocketCurrent {
export interface OctoprintSocketEvent {
event: {
type: string;
payload: unknown;
payload: {
error: string;
reason: string;
};
};
}
export interface OctoprintPluginMessage {
Expand Down
3 changes: 3 additions & 0 deletions src/app/model/system.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ export interface Notification {
text: string;
type: NotificationType;
time: Date;
choices?: Array<string>;
callback?: (index: number) => void;
sticky?: boolean;
}

export enum NotificationType {
INFO,
WARN,
ERROR,
PROMPT,
}

export interface UpdateError {
Expand Down
13 changes: 11 additions & 2 deletions src/app/notification/notification.component.html
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
<div
class="notification"
[ngClass]="['notification__border-' + notification?.type, show ? 'notification__show' : '']"
(click)="hideNotification()">
(click)="hideNotification(true, true)">
<span class="notification__time">{{ notification?.time | date: 'HH:mm' }}</span>
<span class="notification__heading">{{ notification?.heading }}</span>
<span class="notification__text">{{ notification?.text }}</span>
<span class="notification__close" i18n="@@tap-close">tap this card to close it</span>
<span class="notification__close" i18n="@@tap-close" *ngIf="!notification?.choices">tap this card to close it</span>
<div class="notification-prompt__choices">
<div
class="notification-prompt__choice"
*ngFor="let choice of notification?.choices; index as i; first as isFirst"
[ngClass]="[isFirst ? 'notification-prompt__choice-first' : '']"
(click)="chooseAction(i, notification?.callback)">
{{ choice }}
</div>
</div>
</div>
27 changes: 27 additions & 0 deletions src/app/notification/notification.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,29 @@
z-index: 100;
border-right: 1vw solid #5a6675;

&-prompt {
&__choices {
display: flex;
flex-wrap: wrap;
justify-content: space-evenly;
}

&__choice {
display: block;
font-size: 2.7vw;
background-color: #2196f3;
text-align: center;
padding: 3vh;
border-radius: 0.8vw;
margin: 1vw;
flex-grow: 100;

&-first {
background-color: #44bd32;
}
}
}

&__show {
top: 5vh;
}
Expand Down Expand Up @@ -47,6 +70,10 @@
}

&__border {
&-3 {
border-left: 1vw solid #a1abb7;
}

&-2 {
border-left: 1vw solid #c23616;
}
Expand Down
17 changes: 12 additions & 5 deletions src/app/notification/notification.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,17 @@ export class NotificationComponent implements OnDestroy {
);
}

public hideNotification(removeFromStack = true): void {
this.show = false;
clearTimeout(this.notificationCloseTimeout);
if (removeFromStack) this.notificationService.removeNotification(this.notification);
public hideNotification(removeFromStack = true, userTriggered = false): void {
if (!userTriggered || (userTriggered && !this.notification.choices)) {
this.show = false;
clearTimeout(this.notificationCloseTimeout);
if (removeFromStack) this.notificationService.removeNotification(this.notification);
}
}

public chooseAction(index: number, callback: (index: number) => void): void {
callback(index);
this.hideNotification();
}

private setNotification(notification: Notification | 'close'): void {
Expand All @@ -41,7 +48,7 @@ export class NotificationComponent implements OnDestroy {

if (!notification.sticky) {
clearTimeout(this.notificationCloseTimeout);
this.notificationCloseTimeout = setTimeout(this.hideNotification.bind(this), 30 * 1000, false);
this.notificationCloseTimeout = setTimeout(this.hideNotification.bind(this), 15 * 1000, false);
}
}
});
Expand Down
112 changes: 100 additions & 12 deletions src/app/services/socket/socket.octoprint.service.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,30 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import _ from 'lodash-es';
import { Observable, ReplaySubject, Subject } from 'rxjs';
import { pluck, startWith } from 'rxjs/operators';
import { Observable, of, ReplaySubject, Subject } from 'rxjs';
import { catchError, pluck, startWith } from 'rxjs/operators';
import { webSocket, WebSocketSubject } from 'rxjs/webSocket';

import { ConfigService } from '../../config/config.service';
import { ConversionService } from '../../conversion.service';
import { JobStatus, PrinterEvent, PrinterState, PrinterStatus, SocketAuth } from '../../model';
import {
JobStatus,
Notification,
NotificationType,
PrinterEvent,
PrinterNotification,
PrinterState,
PrinterStatus,
SocketAuth,
} from '../../model';
import {
DisplayLayerProgressData,
OctoprintFilament,
OctoprintPluginMessage,
OctoprintSocketCurrent,
OctoprintSocketEvent,
} from '../../model/octoprint';
import { NotificationService } from '../../notification/notification.service';
import { SystemService } from '../system/system.service';
import { SocketService } from './socket.service';

Expand All @@ -36,11 +46,12 @@ export class OctoPrintSocketService implements SocketService {
private configService: ConfigService,
private systemService: SystemService,
private conversionService: ConversionService,
private notificationService: NotificationService,
private http: HttpClient,
) {
this.printerStatusSubject = new ReplaySubject<PrinterStatus>(1);
this.jobStatusSubject = new Subject<JobStatus>();
this.eventSubject = new ReplaySubject<PrinterEvent>(1);
this.eventSubject = new ReplaySubject<PrinterEvent>();
}

//==== SETUP & AUTH ====//
Expand Down Expand Up @@ -118,6 +129,25 @@ export class OctoPrintSocketService implements SocketService {
this.socket.next(payload);
}

private handlePluginMessage(pluginMessage: OctoprintPluginMessage) {
const plugins = [
{
check: (plugin: string) =>
plugin === 'DisplayLayerProgress-websocket-payload' && this.configService.isDisplayLayerProgressEnabled(),
handler: (message: unknown) => {
this.extractFanSpeed(message as DisplayLayerProgressData);
this.extractLayerHeight(message as DisplayLayerProgressData);
},
},
{
check: (plugin: string) => ['action_command_prompt', 'action_command_notification'].includes(plugin),
handler: (message: unknown) => this.handlePrinterNotification(message as PrinterNotification),
},
];

plugins.forEach(plugin => plugin.check(pluginMessage.plugin.plugin) && plugin.handler(pluginMessage.plugin.data));
}

private setupSocket(resolve: () => void) {
this.socket.subscribe({
next: message => {
Expand All @@ -132,14 +162,7 @@ export class OctoPrintSocketService implements SocketService {
} else if (Object.hasOwnProperty.bind(message)('event')) {
this.extractPrinterEvent(message as OctoprintSocketEvent);
} else if (Object.hasOwnProperty.bind(message)('plugin')) {
const pluginMessage = message as OctoprintPluginMessage;
if (
pluginMessage.plugin.plugin === 'DisplayLayerProgress-websocket-payload' &&
this.configService.isDisplayLayerProgressEnabled()
) {
this.extractFanSpeed(pluginMessage.plugin.data as DisplayLayerProgressData);
this.extractLayerHeight(pluginMessage.plugin.data as DisplayLayerProgressData);
}
this.handlePluginMessage(message as OctoprintPluginMessage);
} else if (Object.hasOwnProperty.bind(message)('reauthRequired')) {
this.systemService.getSessionKey().subscribe(socketAuth => this.authenticateSocket(socketAuth));
} else if (Object.hasOwnProperty.bind(message)('connected')) {
Expand Down Expand Up @@ -300,6 +323,15 @@ export class OctoPrintSocketService implements SocketService {
break;
case 'Error':
newState = PrinterEvent.CLOSED;
if (state.event.payload) {
this.notificationService.setNotification({
heading: $localize`:@@printer-information:Printer error`,
text: state.event.payload.error,
type: NotificationType.ERROR,
time: new Date(),
sticky: true,
} as Notification);
}
break;
default:
break;
Expand All @@ -311,6 +343,62 @@ export class OctoPrintSocketService implements SocketService {
}
}

//==== Notifications ====//

private handlePrinterNotification(notification: PrinterNotification) {
if (Object.keys(notification).length > 0) {
if (notification.action === 'close') {
this.notificationService.closeNotification();
} else if (notification.choices?.length > 0) {
this.notificationService.setNotification({
heading: $localize`:@@action-required:Action required`,
text: notification.text ?? notification.message,
type: NotificationType.PROMPT,
time: new Date(),
choices: notification.choices,
callback: this.callbackFunction.bind(this),
sticky: true,
} as Notification);
} else if (notification.choices?.length == 0) {
this.notificationService.setNotification({
heading: $localize`:@@printer-information:Printer information`,
text: notification.text ?? notification.message,
type: NotificationType.WARN,
time: new Date(),
sticky: true,
} as Notification);
} else if (notification.text || notification.message) {
this.notificationService.setNotification({
heading: $localize`:@@printer-information:Printer information`,
text: notification.text ?? notification.message,
type: NotificationType.INFO,
time: new Date(),
} as Notification);
}
}
}

private callbackFunction(index: number) {
this.http
.post(
this.configService.getApiURL('plugin/action_command_prompt'),
{ command: 'select', choice: index },
this.configService.getHTTPHeaders(),
)
.pipe(
catchError(error => {
this.notificationService.setNotification({
heading: $localize`:@@error-answer-prompt:Can't answer prompt!`,
text: error.message,
type: NotificationType.ERROR,
time: new Date(),
});
return of(null);
}),
)
.subscribe();
}

//==== Subscribables ====//

public getPrinterStatusSubscribable(): Observable<PrinterStatus> {
Expand Down
Loading

0 comments on commit 8801423

Please sign in to comment.