2
0
mirror of https://github.com/ad1217/PrinterStatus synced 2024-11-21 15:03:48 -05:00

Add a "settings" message to send header colors and webcam transform

This should ideally be in 3 commits, but oh well.
This commit is contained in:
Adam Goldsmith 2021-11-04 23:28:21 -04:00
parent ecd5185dc4
commit 787ec126c5
5 changed files with 139 additions and 45 deletions

View File

@ -3,7 +3,7 @@ import fetch from 'node-fetch';
/// <reference path="mjpeg-proxy.d.ts"/> /// <reference path="mjpeg-proxy.d.ts"/>
import { MjpegProxy } from 'mjpeg-proxy'; import { MjpegProxy } from 'mjpeg-proxy';
import {ExtendedMessage} from '../../types/messages'; import {Message, StatusMessage, SettingsMessage} from '../../types/messages';
import * as octoprint from '../../types/octoprint'; import * as octoprint from '../../types/octoprint';
const PING_TIME = 10000; const PING_TIME = 10000;
@ -13,13 +13,14 @@ type Timeout = ReturnType<typeof setTimeout>;
export default class OctoprintConnection { export default class OctoprintConnection {
public name?: string; public name?: string;
public webcamProxy?: MjpegProxy; public webcamProxy?: MjpegProxy;
protected lastStatus?: ExtendedMessage; protected lastStatus?: StatusMessage;
protected settingsMessage?: SettingsMessage;
constructor( constructor(
public slug: string, public slug: string,
public address: string, public address: string,
protected apikey: string, protected apikey: string,
protected broadcast: (msg: ExtendedMessage) => void protected broadcast: (msg: Message) => void
) { ) {
this.try_connect_websocket(); this.try_connect_websocket();
} }
@ -41,7 +42,18 @@ export default class OctoprintConnection {
if (this.webcamProxy === undefined) { if (this.webcamProxy === undefined) {
this.webcamProxy = new MjpegProxy(webcamURL.toString()); this.webcamProxy = new MjpegProxy(webcamURL.toString());
} }
this.name = settings.appearance.name; this.settingsMessage = {
kind: "settings",
printer: this.slug,
name: settings.appearance.name,
color: settings.appearance.color,
webcam: {
flipH: settings.webcam.flipH,
flipV: settings.webcam.flipV,
rotate90: settings.webcam.rotate90,
}
}
this.broadcast(this.settingsMessage);
// do passive login to get a session key from the API key // do passive login to get a session key from the API key
const login: octoprint.LoginResponse = await this.api_post('login', { const login: octoprint.LoginResponse = await this.api_post('login', {
@ -66,10 +78,10 @@ export default class OctoprintConnection {
.on('message', (data: WebSocket.Data) => { .on('message', (data: WebSocket.Data) => {
const event: octoprint.Message = JSON.parse(data as string); const event: octoprint.Message = JSON.parse(data as string);
let ext_event: ExtendedMessage = { let ext_event: StatusMessage = {
...event, kind: "status",
printer: this.slug, printer: this.slug,
name: this.name, msg: event,
}; };
this.broadcast(ext_event); this.broadcast(ext_event);
@ -120,12 +132,12 @@ export default class OctoprintConnection {
} }
send_init(ws: WebSocket) { send_init(ws: WebSocket) {
let payload: ExtendedMessage; if (this.settingsMessage) {
ws.send(JSON.stringify(this.settingsMessage));
if (this.lastStatus) { if (this.lastStatus) {
payload = this.lastStatus; ws.send(JSON.stringify(this.lastStatus));
} else { }
payload = { init: null, printer: this.slug, name: this.name };
} }
ws.send(JSON.stringify(payload));
} }
} }

View File

@ -5,7 +5,7 @@ import * as yaml from 'js-yaml';
import * as expressWs from 'express-ws'; import * as expressWs from 'express-ws';
import * as WebSocket from 'ws'; import * as WebSocket from 'ws';
import { ExtendedMessage } from '../../types/messages'; import { Message } from '../../types/messages';
import OctoPrintConnection from './OctoPrintConnection'; import OctoPrintConnection from './OctoPrintConnection';
const PORT = process.env.PORT || 1234; const PORT = process.env.PORT || 1234;
@ -31,7 +31,7 @@ function broadcast(data: WebSocket.Data) {
}); });
} }
function broadcastPayload(payload: ExtendedMessage) { function broadcastPayload(payload: Message) {
broadcast(JSON.stringify(payload)); broadcast(JSON.stringify(payload));
} }

View File

@ -29,17 +29,10 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref, Ref } from 'vue'; import { computed, ref, Ref } from 'vue';
import * as messages from '../types/messages'; import { Message, WebcamSettings } from '../types/messages';
import * as octoprint from '../types/octoprint'; import PrinterCard, { PrinterInfo } from './PrinterCard.vue';
import PrinterCard from './PrinterCard.vue';
const printers: Ref<{ const printers: Ref<{ [key: string]: PrinterInfo }> = ref({});
[key: string]: {
name?: string;
lastUpdate: Date;
status: octoprint.CurrentOrHistoryPayload | null;
};
}> = ref({});
const hasPrinters = computed(() => Object.keys(printers.value).length > 0); const hasPrinters = computed(() => Object.keys(printers.value).length > 0);
let now: Ref<Date> = ref(new Date()); let now: Ref<Date> = ref(new Date());
@ -53,22 +46,30 @@ function connectWebsocket() {
(loc.protocol === 'https:' ? 'wss://' : 'ws://') + loc.host + '/ws'; (loc.protocol === 'https:' ? 'wss://' : 'ws://') + loc.host + '/ws';
websocket = new WebSocket(ws_uri); websocket = new WebSocket(ws_uri);
websocket.addEventListener('message', (ev: MessageEvent) => { websocket.addEventListener('message', (ev: MessageEvent) => {
const event: messages.ExtendedMessage = JSON.parse(ev.data as string); const event: Message = JSON.parse(ev.data as string);
console.log(event); console.log(event);
if (!(event.printer in printers.value)) { if (!(event.printer in printers.value)) {
printers.value[event.printer] = { status: null, lastUpdate: new Date() }; printers.value[event.printer] = {
status: null,
webcam: null,
color: null,
lastUpdate: new Date(),
};
} }
printers.value[event.printer].name = event.name;
printers.value[event.printer].lastUpdate = new Date(); printers.value[event.printer].lastUpdate = new Date();
if ('init' in event) { if (event.kind === 'settings') {
printers.value[event.printer].status = null; printers.value[event.printer].name = event.name;
} else if ('current' in event && event.current) { printers.value[event.printer].webcam = event.webcam;
printers.value[event.printer].status = event.current; printers.value[event.printer].color = event.color;
} else if ('history' in event && event.history) { } else if (event.kind === 'status') {
printers.value[event.printer].status = event.history; if ('current' in event.msg && event.msg.current) {
} else if ('remote_ws_status' in event) { printers.value[event.printer].status = event.msg.current;
} else if ('history' in event.msg && event.msg.history) {
printers.value[event.printer].status = event.msg.history;
} else if ('remote_ws_status' in event.msg) {
}
} }
}); });

View File

@ -1,7 +1,13 @@
<template> <template>
<div class="card"> <div class="card">
<h3 class="card-header">{{ name || 'Unknown' }}</h3> <h3 class="card-header" :data-color="color">
<img class="card-img webcam" :src="'/webcam/' + slug" /> {{ name || 'Unknown' }}
</h3>
<img
class="card-img webcam"
:style="webcamTransform"
:src="'/webcam/' + slug"
/>
<div class="card-body" v-if="status"> <div class="card-body" v-if="status">
<div>{{ status.state.text }}</div> <div>{{ status.state.text }}</div>
<div>Job File Name: {{ status.job.file.name || 'None' }}</div> <div>Job File Name: {{ status.job.file.name || 'None' }}</div>
@ -26,15 +32,22 @@
import { computed } from 'vue'; import { computed } from 'vue';
import prettyMilliseconds from 'pretty-ms'; import prettyMilliseconds from 'pretty-ms';
import * as octoprint from '../types/octoprint'; import { CurrentOrHistoryPayload } from '../types/octoprint';
import { WebcamSettings, OctoprintColor } from '../types/messages';
const props = defineProps<{ interface Props {
slug: string; slug: string;
name?: string; name?: string;
lastUpdate: Date; lastUpdate: Date;
now: Date; now: Date;
status: octoprint.CurrentOrHistoryPayload | null; status: CurrentOrHistoryPayload | null;
}>(); webcam: WebcamSettings | null;
color: OctoprintColor | null;
}
export type PrinterInfo = Omit<Props, 'slug' | 'now'>;
const props = defineProps<Props>();
function formatDuration(seconds: number): string { function formatDuration(seconds: number): string {
return prettyMilliseconds(seconds * 1000); return prettyMilliseconds(seconds * 1000);
@ -54,4 +67,51 @@ const lastUpdateString = computed(() => {
} }
} }
}); });
const webcamTransform = computed(() => {
if (props.webcam) {
const transforms = [];
if (props.webcam.flipH) {
transforms.push('scaleX(-1)');
}
if (props.webcam.flipV) {
transforms.push('scaleY(-1)');
}
if (props.webcam.rotate90) {
transforms.push('rotate(90)');
}
return { transform: transforms.join(' ') };
} else {
return {};
}
});
</script> </script>
<style lang="scss">
$bs-colors: ('red', 'orange', 'yellow', 'green', 'blue', 'white');
.card-header[data-color] {
background-image: var(--bs-gradient);
@each $color in $bs-colors {
&[data-color='#{$color}'] {
background-color: var(--bs-#{$color});
}
}
&[data-color='violet'] {
background-color: var(--bs-purple);
}
&[data-color='black'] {
background-color: var(--bs-purple);
}
&[data-color='blue'],
&[data-color='green'],
&[data-color='violet'],
&[data-color='black'] {
color: var(--bs-light);
}
}
</style>

29
types/messages.d.ts vendored
View File

@ -1,6 +1,27 @@
import { Message } from './octoprint'; import { Message as OctoprintMessage } from './octoprint';
export type ExtendedMessage = (Message | { init?: null }) & { interface BaseMessage {
printer: string; printer: string;
name?: string; }
};
export interface StatusMessage extends BaseMessage {
kind: 'status';
msg: OctoprintMessage;
}
export interface WebcamSettings {
flipH: boolean;
flipV: boolean;
rotate90: boolean;
}
export type OctoprintColor = 'red' | 'orange' | 'yellow' | 'green' | 'blue' | 'violet' | 'black' | 'white' | 'default';
export interface SettingsMessage extends BaseMessage {
kind: 'settings';
name: string;
webcam: WebcamSettings;
color: OctoprintColor;
}
export type Message = StatusMessage | SettingsMessage;