feat: add LED / Neopixel support (#1050)
This commit is contained in:
parent
128baea88c
commit
a88c9ba083
43
package-lock.json
generated
43
package-lock.json
generated
@ -16,6 +16,7 @@
|
||||
"@codemirror/search": "^6.0.0",
|
||||
"@codemirror/state": "^6.1.0",
|
||||
"@codemirror/view": "^6.0.3",
|
||||
"@jaames/iro": "^5.5.2",
|
||||
"@lezer/highlight": "^1.0.0",
|
||||
"@sindarius/gcodeviewer": "^3.1.4",
|
||||
"@types/node": "^18.0.0",
|
||||
@ -2419,6 +2420,20 @@
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/@irojs/iro-core": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@irojs/iro-core/-/iro-core-1.2.1.tgz",
|
||||
"integrity": "sha512-p2OvsBSSmidsDsTSkID6jEyXDF7lcyxPrkh3qBzasBZFpjkYd6kZ3yMWai3MlAaQ3F7li/Et7rSJVV09Fpei+A=="
|
||||
},
|
||||
"node_modules/@jaames/iro": {
|
||||
"version": "5.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@jaames/iro/-/iro-5.5.2.tgz",
|
||||
"integrity": "sha512-Fbi5U4Vdkw6UsF+R3oMlPONqkvUDMkwzh+mX718gQsDFt3+1r1jvGsrfCbedmXAAy0WsjDHOrefK0BkDk99TQg==",
|
||||
"dependencies": {
|
||||
"@irojs/iro-core": "^1.2.1",
|
||||
"preact": "^10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/gen-mapping": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
|
||||
@ -7617,6 +7632,15 @@
|
||||
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/preact": {
|
||||
"version": "10.10.6",
|
||||
"resolved": "https://registry.npmjs.org/preact/-/preact-10.10.6.tgz",
|
||||
"integrity": "sha512-w0mCL5vICUAZrh1DuHEdOWBjxdO62lvcO++jbzr8UhhYcTbFkpegLH9XX+7MadjTl/y0feoqwQ/zAnzkc/EGog==",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/preact"
|
||||
}
|
||||
},
|
||||
"node_modules/prelude-ls": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
||||
@ -11667,6 +11691,20 @@
|
||||
"@intlify/shared": "9.2.2"
|
||||
}
|
||||
},
|
||||
"@irojs/iro-core": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@irojs/iro-core/-/iro-core-1.2.1.tgz",
|
||||
"integrity": "sha512-p2OvsBSSmidsDsTSkID6jEyXDF7lcyxPrkh3qBzasBZFpjkYd6kZ3yMWai3MlAaQ3F7li/Et7rSJVV09Fpei+A=="
|
||||
},
|
||||
"@jaames/iro": {
|
||||
"version": "5.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@jaames/iro/-/iro-5.5.2.tgz",
|
||||
"integrity": "sha512-Fbi5U4Vdkw6UsF+R3oMlPONqkvUDMkwzh+mX718gQsDFt3+1r1jvGsrfCbedmXAAy0WsjDHOrefK0BkDk99TQg==",
|
||||
"requires": {
|
||||
"@irojs/iro-core": "^1.2.1",
|
||||
"preact": "^10.0.0"
|
||||
}
|
||||
},
|
||||
"@jridgewell/gen-mapping": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
|
||||
@ -15422,6 +15460,11 @@
|
||||
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
|
||||
"dev": true
|
||||
},
|
||||
"preact": {
|
||||
"version": "10.10.6",
|
||||
"resolved": "https://registry.npmjs.org/preact/-/preact-10.10.6.tgz",
|
||||
"integrity": "sha512-w0mCL5vICUAZrh1DuHEdOWBjxdO62lvcO++jbzr8UhhYcTbFkpegLH9XX+7MadjTl/y0feoqwQ/zAnzkc/EGog=="
|
||||
},
|
||||
"prelude-ls": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
||||
|
@ -31,6 +31,7 @@
|
||||
"@codemirror/search": "^6.0.0",
|
||||
"@codemirror/state": "^6.1.0",
|
||||
"@codemirror/view": "^6.0.3",
|
||||
"@jaames/iro": "^5.5.2",
|
||||
"@lezer/highlight": "^1.0.0",
|
||||
"@sindarius/gcodeviewer": "^3.1.4",
|
||||
"@types/node": "^18.0.0",
|
||||
|
@ -97,7 +97,9 @@ import {
|
||||
mdiTune,
|
||||
mdiVideo3d,
|
||||
mdiWebcam,
|
||||
mdiDipSwitch,
|
||||
} from '@mdi/js'
|
||||
import SettingsMiscellaneousTab from '@/components/settings/SettingsMiscellaneousTab.vue'
|
||||
@Component({
|
||||
components: {
|
||||
Panel,
|
||||
@ -113,6 +115,7 @@ import {
|
||||
SettingsGCodeViewerTab,
|
||||
SettingsEditorTab,
|
||||
SettingsTimelapseTab,
|
||||
SettingsMiscellaneousTab,
|
||||
},
|
||||
})
|
||||
export default class TheSettingsMenu extends Mixins(BaseMixin) {
|
||||
@ -186,6 +189,11 @@ export default class TheSettingsMenu extends Mixins(BaseMixin) {
|
||||
name: 'editor',
|
||||
title: this.$t('Settings.EditorTab.Editor'),
|
||||
},
|
||||
{
|
||||
icon: mdiDipSwitch,
|
||||
name: 'miscellaneous',
|
||||
title: this.$t('Settings.MiscellaneousTab.Miscellaneous'),
|
||||
},
|
||||
]
|
||||
|
||||
if (this.moonrakerComponents.includes('timelapse')) {
|
||||
|
63
src/components/inputs/ColorPicker.vue
Normal file
63
src/components/inputs/ColorPicker.vue
Normal file
@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<div>
|
||||
<div ref="picker"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Mixins, Prop, Ref, Watch } from 'vue-property-decorator'
|
||||
import BaseMixin from '@/components/mixins/base'
|
||||
import iro from '@jaames/iro'
|
||||
import { IroColor } from '@irojs/iro-core'
|
||||
import { ColorPickerProps, IroColorPicker as IroCP } from '@jaames/iro/dist/ColorPicker.d'
|
||||
|
||||
@Component
|
||||
export default class ColorPicker extends Mixins(BaseMixin) {
|
||||
colorPicker: IroCP | null = null
|
||||
|
||||
@Ref('picker')
|
||||
readonly picker!: HTMLElement
|
||||
|
||||
@Prop({ type: [Object, String], default: '#ffffff' })
|
||||
readonly color!: IroColor
|
||||
|
||||
@Prop({ type: Object, default: () => ({}) })
|
||||
readonly options!: ColorPickerProps
|
||||
|
||||
@Watch('color', { deep: true })
|
||||
colorChanged(value: string) {
|
||||
if (this.colorPicker && this.colorPicker.color.rgbString !== value) {
|
||||
this.colorPicker.color.rgbString = value
|
||||
}
|
||||
}
|
||||
|
||||
get internalOptions(): ColorPickerProps {
|
||||
return {
|
||||
...this.options,
|
||||
color: this.color,
|
||||
borderWidth: 2,
|
||||
sliderSize: 16,
|
||||
}
|
||||
}
|
||||
|
||||
emitColorChange(color: IroColor) {
|
||||
this.$emit('change', color)
|
||||
this.$emit('update:color', color)
|
||||
}
|
||||
|
||||
onColorChange(color: IroColor) {
|
||||
this.emitColorChange(color)
|
||||
}
|
||||
|
||||
mounted() {
|
||||
this.colorPicker = iro.ColorPicker(this.picker, this.internalOptions)
|
||||
this.colorPicker.on('color:change', this.onColorChange)
|
||||
}
|
||||
|
||||
beforeDestroy() {
|
||||
this.colorPicker?.off('color:change', this.onColorChange)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
519
src/components/inputs/MiscellaneousLight.vue
Normal file
519
src/components/inputs/MiscellaneousLight.vue
Normal file
@ -0,0 +1,519 @@
|
||||
<template>
|
||||
<v-container :class="containerClass">
|
||||
<v-row>
|
||||
<v-col class="pb-3">
|
||||
<v-subheader class="_light-subheader">
|
||||
<v-icon v-if="(!root || groups.length === 0) && isOn" small left @click="off">
|
||||
{{ mdiLightbulbOnOutline }}
|
||||
</v-icon>
|
||||
<v-icon v-else-if="!root || groups.length === 0" small left @click="on">
|
||||
{{ mdiLightbulbOutline }}
|
||||
</v-icon>
|
||||
<span>{{ name }}</span>
|
||||
<v-spacer></v-spacer>
|
||||
<span
|
||||
v-if="!root || groups.length === 0"
|
||||
class="_currentState"
|
||||
:style="currentStateStyle"
|
||||
@click="boolDialog = true"></span>
|
||||
</v-subheader>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<template v-if="root && groups.length">
|
||||
<miscellaneous-light v-for="group in groups" :key="group.id" :object="object" :group="group" />
|
||||
</template>
|
||||
<v-dialog v-model="boolDialog" persistent :width="400">
|
||||
<panel
|
||||
:title="name"
|
||||
:icon="mdiLightbulbOutline"
|
||||
card-class="temperature-edit-heater-dialog"
|
||||
:margin-bottom="false">
|
||||
<template #buttons>
|
||||
<v-btn icon tile @click="boolDialog = false">
|
||||
<v-icon>{{ mdiCloseThick }}</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-card-text class="pt-6">
|
||||
<template v-if="presets.length">
|
||||
<v-row>
|
||||
<v-col class="light-presets-container pt-0 d-flex flex-wrap flex-row justify-center">
|
||||
<v-tooltip v-for="preset in presets" :key="preset.id" top>
|
||||
<template #activator="{ on, attrs }">
|
||||
<div
|
||||
:style="presetStyle(preset)"
|
||||
v-bind="attrs"
|
||||
v-on="on"
|
||||
@click="usePreset(preset)"></div>
|
||||
</template>
|
||||
<span>{{ preset.name }}</span>
|
||||
</v-tooltip>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-divider class="my-3"></v-divider>
|
||||
</template>
|
||||
<v-row>
|
||||
<v-col class="text-center">
|
||||
<color-picker
|
||||
:color="colorRGB"
|
||||
:options="colorPickerOptions"
|
||||
@update:color="onColorRGBChanged" />
|
||||
<color-picker
|
||||
v-if="existWhite"
|
||||
:color="colorRGBW"
|
||||
:options="colorPickerWhiteOptions"
|
||||
class="mt-3"
|
||||
@update:color="onColorWhiteChanged" />
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-row v-if="existRed">
|
||||
<v-col>
|
||||
<number-input
|
||||
:label="$t('Panels.MiscellaneousPanel.Light.Red')"
|
||||
param="red"
|
||||
:target="redInt"
|
||||
:default-value="Math.round(object.initialRed * 255)"
|
||||
:min="0"
|
||||
:max="255"
|
||||
:dec="1"
|
||||
:step="1"
|
||||
:output-error-msg="true"
|
||||
:has-spinner="true"
|
||||
@submit="onColorInput" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-if="existGreen">
|
||||
<v-col>
|
||||
<number-input
|
||||
:label="$t('Panels.MiscellaneousPanel.Light.Green')"
|
||||
param="green"
|
||||
:target="greenInt"
|
||||
:default-value="Math.round(object.initialGreen * 255)"
|
||||
:min="0"
|
||||
:max="255"
|
||||
:dec="1"
|
||||
:step="1"
|
||||
:has-spinner="true"
|
||||
@submit="onColorInput" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-if="existBlue">
|
||||
<v-col>
|
||||
<number-input
|
||||
:label="$t('Panels.MiscellaneousPanel.Light.Blue')"
|
||||
param="blue"
|
||||
:target="blueInt"
|
||||
:default-value="Math.round(object.initialBlue * 255)"
|
||||
:min="0"
|
||||
:max="255"
|
||||
:dec="1"
|
||||
:step="1"
|
||||
:has-spinner="true"
|
||||
@submit="onColorInput" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-if="existWhite">
|
||||
<v-col>
|
||||
<number-input
|
||||
:label="$t('Panels.MiscellaneousPanel.Light.White')"
|
||||
param="white"
|
||||
:target="whiteInt"
|
||||
:default-value="Math.round(object.initialWhite * 255)"
|
||||
:min="0"
|
||||
:max="255"
|
||||
:dec="1"
|
||||
:step="1"
|
||||
:has-spinner="true"
|
||||
@submit="onColorInput" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
</panel>
|
||||
</v-dialog>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { convertName } from '@/plugins/helpers'
|
||||
import { Component, Mixins, Prop } from 'vue-property-decorator'
|
||||
import BaseMixin from '@/components/mixins/base'
|
||||
import { mdiCloseThick, mdiLightbulbOutline, mdiLightbulbOnOutline } from '@mdi/js'
|
||||
import { PrinterStateLight } from '@/store/printer/types'
|
||||
import ColorPicker from '@/components/inputs/ColorPicker.vue'
|
||||
import { ColorPickerProps } from '@jaames/iro/dist/ColorPicker.d'
|
||||
import { Debounce } from 'vue-debounce-decorator'
|
||||
import iro from '@jaames/iro'
|
||||
import { IroColor } from '@irojs/iro-core'
|
||||
import { GuiMiscellaneousStateEntryLightgroup, GuiMiscellaneousStateEntryPreset } from '@/store/gui/miscellaneous/types'
|
||||
|
||||
interface ColorData {
|
||||
red: number | null
|
||||
green: number | null
|
||||
blue: number | null
|
||||
white: number | null
|
||||
}
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
ColorPicker,
|
||||
},
|
||||
})
|
||||
export default class MiscellaneousLight extends Mixins(BaseMixin) {
|
||||
mdiCloseThick = mdiCloseThick
|
||||
mdiLightbulbOutline = mdiLightbulbOutline
|
||||
mdiLightbulbOnOutline = mdiLightbulbOnOutline
|
||||
|
||||
@Prop({ type: Object, required: true })
|
||||
declare object: PrinterStateLight
|
||||
|
||||
@Prop({ type: Boolean, default: false }) readonly root!: boolean
|
||||
@Prop(Object) readonly group!: GuiMiscellaneousStateEntryLightgroup | undefined
|
||||
|
||||
private boolDialog = false
|
||||
private inputValue = 0
|
||||
|
||||
get name() {
|
||||
if (this.group) return convertName(this.group.name)
|
||||
|
||||
return convertName(this.object.name)
|
||||
}
|
||||
|
||||
get colorPickerOptions() {
|
||||
let options: ColorPickerProps = {
|
||||
width: 200,
|
||||
margin: 15,
|
||||
layout: [],
|
||||
}
|
||||
|
||||
if (this.existRed) {
|
||||
// @ts-ignore
|
||||
options?.layout.push({
|
||||
component: iro.ui.Slider,
|
||||
options: {
|
||||
sliderType: 'red',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if (this.existGreen) {
|
||||
// @ts-ignore
|
||||
options?.layout.push({
|
||||
component: iro.ui.Slider,
|
||||
options: {
|
||||
sliderType: 'green',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if (this.existBlue) {
|
||||
// @ts-ignore
|
||||
options?.layout.push({
|
||||
component: iro.ui.Slider,
|
||||
options: {
|
||||
sliderType: 'blue',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if (this.existRed && this.existGreen && this.existBlue) {
|
||||
options.layout = [
|
||||
{
|
||||
component: iro.ui.Wheel,
|
||||
},
|
||||
{
|
||||
component: iro.ui.Slider,
|
||||
options: {
|
||||
sliderType: 'value',
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
get colorPickerWhiteOptions() {
|
||||
let options: ColorPickerProps = {
|
||||
width: 200,
|
||||
margin: 15,
|
||||
layout: [
|
||||
{
|
||||
component: iro.ui.Slider,
|
||||
options: {
|
||||
sliderType: 'alpha',
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
get optionsColors() {
|
||||
let output: string[] = []
|
||||
|
||||
this.presets.forEach((preset: GuiMiscellaneousStateEntryPreset) => {
|
||||
output.push(`rgb(${preset.red}%, ${preset.green}%, ${preset.blue}%)`)
|
||||
})
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
get current() {
|
||||
const color: ColorData = {
|
||||
red: 0,
|
||||
green: 0,
|
||||
blue: 0,
|
||||
white: null,
|
||||
}
|
||||
|
||||
if (this.existWhite) color.white = 0
|
||||
if (this.object.colorData.length === 0) return color
|
||||
|
||||
const firstColorData = this.object.colorData[(this.group?.start ?? 1) - 1]
|
||||
color.red = firstColorData[0] * 255
|
||||
color.green = firstColorData[1] * 255
|
||||
color.blue = firstColorData[2] * 255
|
||||
if (this.object.colorOrder.indexOf('W') !== -1) color.white = firstColorData[3] * 255
|
||||
|
||||
return color
|
||||
}
|
||||
|
||||
get isOn() {
|
||||
return (
|
||||
(this.current.red ?? 0) +
|
||||
(this.current?.green ?? 0) +
|
||||
(this.current.blue ?? 0) +
|
||||
(this.current.white ?? 0) >
|
||||
0
|
||||
)
|
||||
}
|
||||
|
||||
get existRed() {
|
||||
return this.object.colorOrder.indexOf('R') !== -1
|
||||
}
|
||||
|
||||
get existGreen() {
|
||||
return this.object.colorOrder.indexOf('G') !== -1
|
||||
}
|
||||
|
||||
get existBlue() {
|
||||
return this.object.colorOrder.indexOf('B') !== -1
|
||||
}
|
||||
|
||||
get existWhite() {
|
||||
return this.object.colorOrder.indexOf('W') !== -1
|
||||
}
|
||||
|
||||
get currentStateStyle() {
|
||||
let output = this.colorRGB
|
||||
|
||||
if (this.current.white !== null && this.current.red == 0 && this.current.green == 0 && this.current.blue == 0)
|
||||
output = `rgb(${this.current.white * 255}, ${this.current.white * 255}, ${this.current.white * 255})`
|
||||
|
||||
return {
|
||||
'background-color': output,
|
||||
}
|
||||
}
|
||||
|
||||
get colorRGB() {
|
||||
return `rgb(${Math.round(this.current.red ?? 0)}, ${Math.round(this.current.green ?? 0)}, ${Math.round(
|
||||
this.current.blue ?? 0
|
||||
)})`
|
||||
}
|
||||
|
||||
get colorRGBW() {
|
||||
return `rgba(255, 255, 255, ${(this.current.white ?? 0) / 255})`
|
||||
}
|
||||
|
||||
get redInt() {
|
||||
return Math.round(this.current.red ?? 0)
|
||||
}
|
||||
|
||||
get greenInt() {
|
||||
return Math.round(this.current.green ?? 0)
|
||||
}
|
||||
|
||||
get blueInt() {
|
||||
return Math.round(this.current.blue ?? 0)
|
||||
}
|
||||
|
||||
get whiteInt() {
|
||||
return Math.round(this.current.white ?? 0)
|
||||
}
|
||||
|
||||
get groups() {
|
||||
return (
|
||||
this.$store.getters['gui/miscellaneous/getEntryLightgroups']({
|
||||
type: this.object.type,
|
||||
name: this.object.name,
|
||||
}) ?? []
|
||||
)
|
||||
}
|
||||
|
||||
get presets() {
|
||||
return (
|
||||
this.$store.getters['gui/miscellaneous/getEntryPresets']({
|
||||
type: this.object.type,
|
||||
name: this.object.name,
|
||||
}) ?? []
|
||||
)
|
||||
}
|
||||
|
||||
get containerClass() {
|
||||
let output = ['px-0']
|
||||
|
||||
output.push(this.root ? 'py-2' : 'pt-2 pb-0')
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
colorChanged(color: ColorData) {
|
||||
if (
|
||||
Math.round(color.red ?? 0) === Math.round(this.current.red ?? 0) &&
|
||||
Math.round(color.green ?? 0) === Math.round(this.current.green ?? 0) &&
|
||||
Math.round(color.blue ?? 0) === Math.round(this.current.blue ?? 0) &&
|
||||
Math.round(color.white ?? 0) === Math.round(this.current.white ?? 0)
|
||||
)
|
||||
return
|
||||
|
||||
const red = Math.round(((color.red ?? 0) / 255) * 10000) / 10000
|
||||
const green = Math.round(((color.green ?? 0) / 255) * 10000) / 10000
|
||||
const blue = Math.round(((color.blue ?? 0) / 255) * 10000) / 10000
|
||||
const white = Math.round(((color.white ?? 0) / 255) * 10000) / 10000
|
||||
|
||||
let gcode = `SET_LED LED="${this.object.name}" RED=${red} GREEN=${green} BLUE=${blue}`
|
||||
if (this.existWhite) gcode += ` WHITE=${white}`
|
||||
gcode += ` SYNC=0`
|
||||
|
||||
if (this.group) {
|
||||
const tmp = gcode
|
||||
|
||||
for (let i = this.group.start; i <= this.group.end; i++) {
|
||||
if (i === this.group.start) {
|
||||
gcode += ` INDEX=${i}`
|
||||
continue
|
||||
}
|
||||
|
||||
gcode += `\n${tmp} INDEX=${i}`
|
||||
}
|
||||
}
|
||||
|
||||
gcode += ` TRANSMIT=1`
|
||||
|
||||
this.$store.dispatch('server/addEvent', {
|
||||
message: gcode,
|
||||
type: 'command',
|
||||
})
|
||||
this.$socket.emit('printer.gcode.script', { script: gcode })
|
||||
}
|
||||
|
||||
@Debounce({ time: 500 })
|
||||
onColorRGBChanged(payload: IroColor) {
|
||||
const color: ColorData = {
|
||||
red: payload.red,
|
||||
green: payload.green,
|
||||
blue: payload.blue,
|
||||
white: this.current.white,
|
||||
}
|
||||
|
||||
this.colorChanged(color)
|
||||
}
|
||||
|
||||
@Debounce({ time: 500 })
|
||||
onColorWhiteChanged(payload: IroColor) {
|
||||
const color: ColorData = {
|
||||
red: this.current.red,
|
||||
green: this.current.green,
|
||||
blue: this.current.blue,
|
||||
white: this.current.white,
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
color.white = payload.alpha * 255
|
||||
|
||||
this.colorChanged(color)
|
||||
}
|
||||
|
||||
@Debounce({ time: 500 })
|
||||
onColorInput(payload: { name: string; value: number }) {
|
||||
const color: ColorData = {
|
||||
red: this.current.red,
|
||||
green: this.current.green,
|
||||
blue: this.current.blue,
|
||||
white: this.current.white,
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
color[payload.name] = payload.value
|
||||
|
||||
this.colorChanged(color)
|
||||
}
|
||||
|
||||
off() {
|
||||
const color: ColorData = {
|
||||
red: 0,
|
||||
green: 0,
|
||||
blue: 0,
|
||||
white: 0,
|
||||
}
|
||||
|
||||
this.colorChanged(color)
|
||||
}
|
||||
|
||||
on() {
|
||||
const color: ColorData = {
|
||||
red: 255,
|
||||
green: 255,
|
||||
blue: 255,
|
||||
white: 255,
|
||||
}
|
||||
|
||||
this.colorChanged(color)
|
||||
}
|
||||
|
||||
presetStyle(preset: GuiMiscellaneousStateEntryPreset) {
|
||||
if ((preset?.red ?? 0) + (preset?.green ?? 0) + (preset?.blue ?? 0) === 0 && (preset?.white ?? 0) > 0) {
|
||||
return {
|
||||
backgroundColor: `rgb(${preset.white}%, ${preset.white}%, ${preset.white}%)`,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
backgroundColor: `rgb(${preset.red}%, ${preset.green}%, ${preset.blue}%)`,
|
||||
}
|
||||
}
|
||||
|
||||
usePreset(preset: GuiMiscellaneousStateEntryPreset) {
|
||||
const color: ColorData = { ...preset }
|
||||
|
||||
this.colorChanged(color)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
._light-subheader {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
._currentState {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid lightgray;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.light-presets-container {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.light-presets-container > div {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
@ -3,7 +3,18 @@
|
||||
<v-row>
|
||||
<v-col :class="pwm ? 'pb-1' : 'pb-3'">
|
||||
<v-subheader class="_fan-slider-subheader">
|
||||
<v-icon v-if="type !== 'output_pin'" small :class="fanClasses">{{ mdiFan }}</v-icon>
|
||||
<v-icon
|
||||
v-if="type === 'led' && target > 0"
|
||||
class="mr-2"
|
||||
small
|
||||
:retain-focus-on-click="true"
|
||||
@click="ledOff">
|
||||
{{ mdiLightbulbOnOutline }}
|
||||
</v-icon>
|
||||
<v-icon v-else-if="type === 'led'" class="mr-2" small :retain-focus-on-click="true" @click="ledOn">
|
||||
{{ mdiLightbulbOutline }}
|
||||
</v-icon>
|
||||
<v-icon v-else-if="type !== 'output_pin'" small :class="fanClasses">{{ mdiFan }}</v-icon>
|
||||
<span>{{ convertName(name) }}</span>
|
||||
<v-spacer></v-spacer>
|
||||
<small v-if="rpm !== null" :class="rpmClasses">{{ Math.round(rpm ?? 0) }} RPM</small>
|
||||
@ -87,6 +98,8 @@ import {
|
||||
mdiPlus,
|
||||
mdiToggleSwitch,
|
||||
mdiToggleSwitchOffOutline,
|
||||
mdiLightbulbOutline,
|
||||
mdiLightbulbOnOutline,
|
||||
} from '@mdi/js'
|
||||
|
||||
@Component
|
||||
@ -98,6 +111,8 @@ export default class MiscellaneousSlider extends Mixins(BaseMixin) {
|
||||
mdiLockOpenVariantOutline = mdiLockOpenVariantOutline
|
||||
mdiMinus = mdiMinus
|
||||
mdiPlus = mdiPlus
|
||||
mdiLightbulbOutline = mdiLightbulbOutline
|
||||
mdiLightbulbOnOutline = mdiLightbulbOnOutline
|
||||
|
||||
convertName = convertName
|
||||
private declare timeout: ReturnType<typeof setTimeout>
|
||||
@ -135,6 +150,9 @@ export default class MiscellaneousSlider extends Mixins(BaseMixin) {
|
||||
@Prop({ type: Number, default: 0 })
|
||||
declare off_below: number
|
||||
|
||||
@Prop({ type: String, default: '' })
|
||||
declare colorOrder: string
|
||||
|
||||
get value(): number {
|
||||
return Math.round((this.target / this.max) * 100) / 100
|
||||
}
|
||||
@ -187,6 +205,8 @@ export default class MiscellaneousSlider extends Mixins(BaseMixin) {
|
||||
if (this.type === 'fan') gcode = `M106 S${newVal.toFixed(0)}`
|
||||
if (this.type === 'fan_generic') gcode = `SET_FAN_SPEED FAN=${this.name} SPEED=${newVal}`
|
||||
if (this.type === 'output_pin') gcode = `SET_PIN PIN=${this.name} VALUE=${newVal.toFixed(2)}`
|
||||
if (this.type === 'led')
|
||||
gcode = `SET_LED LED=${this.name} ${this.ledChannelName}=${newVal.toFixed(2)} SYNC=0 TRANSMIT=1`
|
||||
|
||||
if (gcode !== '') {
|
||||
this.$store.dispatch('server/addEvent', { message: gcode, type: 'command' })
|
||||
@ -196,6 +216,14 @@ export default class MiscellaneousSlider extends Mixins(BaseMixin) {
|
||||
this.startLockTimer()
|
||||
}
|
||||
|
||||
ledOff() {
|
||||
this.sendCmd(0)
|
||||
}
|
||||
|
||||
ledOn() {
|
||||
this.sendCmd(1)
|
||||
}
|
||||
|
||||
switchOutputPin(): void {
|
||||
const newVal = this.value ? 0 : 1
|
||||
const gcode = `SET_PIN PIN=${this.name} VALUE=${(newVal * this.multi).toFixed(2)}`
|
||||
@ -266,6 +294,14 @@ export default class MiscellaneousSlider extends Mixins(BaseMixin) {
|
||||
return output
|
||||
}
|
||||
|
||||
get ledChannelName() {
|
||||
if (this.colorOrder === 'R') return 'RED'
|
||||
if (this.colorOrder === 'G') return 'GREEN'
|
||||
if (this.colorOrder === 'B') return 'BLUE'
|
||||
|
||||
return 'WHITE'
|
||||
}
|
||||
|
||||
submitInput(): void {
|
||||
if (this.errors.length > 0) return
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<template>
|
||||
<panel
|
||||
v-if="klipperReadyForGui && (miscellaneous.length || filamentSensors.length)"
|
||||
v-if="showMiscellaneousPanel"
|
||||
:icon="mdiDipSwitch"
|
||||
:title="$t('Panels.MiscellaneousPanel.Headline')"
|
||||
:collapsible="true"
|
||||
@ -20,8 +20,21 @@
|
||||
:max="object.max_power"
|
||||
:multi="parseInt(object.scale)"></miscellaneous-slider>
|
||||
</div>
|
||||
<div v-for="(sensor, index) of filamentSensors" :key="'sensor_' + index">
|
||||
<div v-for="(light, index) of lights" :key="'light_' + light.name">
|
||||
<v-divider v-if="index || miscellaneous.length"></v-divider>
|
||||
<miscellaneous-slider
|
||||
v-if="light.type === 'led' && light.colorOrder.length === 1"
|
||||
:name="light.name"
|
||||
type="led"
|
||||
:rpm="null"
|
||||
:controllable="true"
|
||||
:pwm="true"
|
||||
:target="light.singleChannelTarget"
|
||||
:color-order="light.colorOrder" />
|
||||
<miscellaneous-light v-else :object="light" :root="true" />
|
||||
</div>
|
||||
<div v-for="(sensor, index) of filamentSensors" :key="'sensor_' + index">
|
||||
<v-divider v-if="index || miscellaneous.length || lights.length"></v-divider>
|
||||
<filament-sensor
|
||||
:name="sensor.name"
|
||||
:enabled="sensor.enabled"
|
||||
@ -34,21 +47,32 @@
|
||||
import { Component, Mixins } from 'vue-property-decorator'
|
||||
import BaseMixin from '@/components/mixins/base'
|
||||
import MiscellaneousSlider from '@/components/inputs/MiscellaneousSlider.vue'
|
||||
import MiscellaneousLight from '@/components/inputs/MiscellaneousLight.vue'
|
||||
import FilamentSensor from '@/components/inputs/FilamentSensor.vue'
|
||||
import Panel from '@/components/ui/Panel.vue'
|
||||
import { mdiDipSwitch } from '@mdi/js'
|
||||
@Component({
|
||||
components: { Panel, FilamentSensor, MiscellaneousSlider },
|
||||
components: { Panel, FilamentSensor, MiscellaneousSlider, MiscellaneousLight },
|
||||
})
|
||||
export default class MiscellaneousPanel extends Mixins(BaseMixin) {
|
||||
mdiDipSwitch = mdiDipSwitch
|
||||
|
||||
get filamentSensors() {
|
||||
return this.$store.getters['printer/getFilamentSensors'] ?? []
|
||||
}
|
||||
|
||||
get miscellaneous() {
|
||||
return this.$store.getters['printer/getMiscellaneous'] ?? []
|
||||
}
|
||||
|
||||
get filamentSensors() {
|
||||
return this.$store.getters['printer/getFilamentSensors'] ?? []
|
||||
get lights() {
|
||||
return this.$store.getters['printer/getLights'] ?? []
|
||||
}
|
||||
|
||||
get showMiscellaneousPanel() {
|
||||
return (
|
||||
this.klipperReadyForGui && (this.miscellaneous.length || this.filamentSensors.length || this.lights.length)
|
||||
)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
81
src/components/settings/SettingsMiscellaneousTab.vue
Normal file
81
src/components/settings/SettingsMiscellaneousTab.vue
Normal file
@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<div>
|
||||
<settings-miscellaneous-tab-light-groups
|
||||
v-if="editLightGroupObject"
|
||||
:light="editLightGroupObject"
|
||||
@close="editLightGroupObject = null" />
|
||||
<settings-miscellaneous-tab-light-presets
|
||||
v-else-if="editLightPresetObject"
|
||||
:light="editLightPresetObject"
|
||||
@close="editLightPresetObject = null" />
|
||||
<v-card-text v-else>
|
||||
<h3 class="text-h5 mb-3">{{ $t('Settings.MiscellaneousTab.Miscellaneous') }}</h3>
|
||||
<template v-if="filteredLights.length">
|
||||
<div v-for="(light, index) in filteredLights" :key="index">
|
||||
<v-divider v-if="index" class="my-2"></v-divider>
|
||||
<settings-row :title="convertName(light.name)" :dynamic-slot-width="true">
|
||||
<v-btn
|
||||
v-if="light.chainCount > 1"
|
||||
small
|
||||
outlined
|
||||
class="ml-3"
|
||||
@click="editLightGroupObject = light">
|
||||
<v-icon left small>{{ mdiPencil }}</v-icon>
|
||||
{{ $t('Settings.MiscellaneousTab.Groups') }}
|
||||
</v-btn>
|
||||
<v-btn small outlined class="ml-3" @click="editLightPresetObject = light">
|
||||
<v-icon left small>{{ mdiPalette }}</v-icon>
|
||||
{{ $t('Settings.MiscellaneousTab.Presets') }}
|
||||
</v-btn>
|
||||
</settings-row>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<p class="mb-0 text-center font-italic">{{ $t('Settings.MiscellaneousTab.NoDevicesFound') }}</p>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</template>
|
||||
</v-card-text>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Mixins } from 'vue-property-decorator'
|
||||
import BaseMixin from '../mixins/base'
|
||||
import SettingsRow from '@/components/settings/SettingsRow.vue'
|
||||
import { mdiDelete, mdiPalette, mdiPencil } from '@mdi/js'
|
||||
import { convertName } from '@/plugins/helpers'
|
||||
import SettingsMiscellaneousTabLightGroups from '@/components/settings/SettingsMiscellaneousTabLightGroups.vue'
|
||||
import SettingsMiscellaneousTabLightPresets from '@/components/settings/SettingsMiscellaneousTabLightPresets.vue'
|
||||
import { PrinterStateLight } from '@/store/printer/types'
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
SettingsRow,
|
||||
SettingsMiscellaneousTabLightGroups,
|
||||
SettingsMiscellaneousTabLightPresets,
|
||||
},
|
||||
})
|
||||
export default class SettingsMiscellaneousTab extends Mixins(BaseMixin) {
|
||||
mdiDelete = mdiDelete
|
||||
mdiPalette = mdiPalette
|
||||
mdiPencil = mdiPencil
|
||||
|
||||
convertName = convertName
|
||||
|
||||
editLightGroupObject: PrinterStateLight | null = null
|
||||
editLightPresetObject: PrinterStateLight | null = null
|
||||
|
||||
get lights() {
|
||||
return this.$store.getters['printer/getLights'] ?? []
|
||||
}
|
||||
|
||||
get filteredLights() {
|
||||
return this.lights.filter((light: PrinterStateLight) => light.colorOrder.length > 1)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
236
src/components/settings/SettingsMiscellaneousTabLightGroups.vue
Normal file
236
src/components/settings/SettingsMiscellaneousTabLightGroups.vue
Normal file
@ -0,0 +1,236 @@
|
||||
<template>
|
||||
<div>
|
||||
<template v-if="boolForm">
|
||||
<v-card-text>
|
||||
<h3 class="text-h5 mb-3">{{ $t('Settings.MiscellaneousTab.CreateGroup') }}</h3>
|
||||
<settings-row :title="$t('Settings.MiscellaneousTab.Name').toString()">
|
||||
<v-text-field
|
||||
v-model="form.name"
|
||||
hide-details="auto"
|
||||
:rules="[rules.required, rules.groupUnique]"
|
||||
dense
|
||||
outlined></v-text-field>
|
||||
</settings-row>
|
||||
<v-divider class="my-2"></v-divider>
|
||||
<settings-row
|
||||
:title="$t('Settings.MiscellaneousTab.Start').toString()"
|
||||
:sub-title="$t('Settings.MiscellaneousTab.StartDescription').toString()">
|
||||
<v-text-field
|
||||
v-model="form.start"
|
||||
hide-details="auto"
|
||||
type="number"
|
||||
step="1"
|
||||
:rules="[rules.minStart, rules.max]"
|
||||
dense
|
||||
outlined></v-text-field>
|
||||
</settings-row>
|
||||
<v-divider class="my-2"></v-divider>
|
||||
<settings-row
|
||||
:title="$t('Settings.MiscellaneousTab.End').toString()"
|
||||
:sub-title="$t('Settings.MiscellaneousTab.EndDescription').toString()">
|
||||
<v-text-field
|
||||
v-model="form.end"
|
||||
hide-details="auto"
|
||||
type="number"
|
||||
step="1"
|
||||
:rules="[rules.minEnd, rules.max]"
|
||||
dense
|
||||
outlined></v-text-field>
|
||||
</settings-row>
|
||||
</v-card-text>
|
||||
<v-card-actions class="d-flex justify-end">
|
||||
<v-btn text @click="closeForm">{{ $t('Settings.Cancel') }}</v-btn>
|
||||
<v-btn v-if="form.id !== null" text color="primary" @click="updateGroup">
|
||||
{{ $t('Settings.Update') }}
|
||||
</v-btn>
|
||||
<v-btn v-else text color="primary" @click="storeGroup">{{ $t('Settings.Store') }}</v-btn>
|
||||
</v-card-actions>
|
||||
</template>
|
||||
<template v-else>
|
||||
<v-card-text>
|
||||
<h3 class="text-h5 mb-3">{{ $t('Settings.MiscellaneousTab.LightGroups', { name: light.name }) }}</h3>
|
||||
<template v-if="light">
|
||||
<template v-if="groups.length">
|
||||
<div v-for="(group, index) in groups" :key="group.id">
|
||||
<v-divider v-if="index" class="my-2"></v-divider>
|
||||
<settings-row
|
||||
:title="group.name"
|
||||
:sub-title="
|
||||
$t('Settings.MiscellaneousTab.GroupSubTitle', {
|
||||
start: group.start,
|
||||
end: group.end,
|
||||
}).toString()
|
||||
"
|
||||
:dynamic-slot-width="true">
|
||||
<v-btn small outlined class="ml-3" @click="editGroup(group)">
|
||||
<v-icon left small>{{ mdiPencil }}</v-icon>
|
||||
{{ $t('Settings.Edit') }}
|
||||
</v-btn>
|
||||
<v-btn
|
||||
small
|
||||
outlined
|
||||
class="ml-3 minwidth-0 px-2"
|
||||
color="error"
|
||||
@click="deleteGroup(group.id)">
|
||||
<v-icon small>{{ mdiDelete }}</v-icon>
|
||||
</v-btn>
|
||||
</settings-row>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<p class="mb-0 text-center font-italic">
|
||||
{{ $t('Settings.MiscellaneousTab.NoGroupFound') }}
|
||||
</p>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</template>
|
||||
</template>
|
||||
<template v-else>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<p class="mb-0 text-center font-italic">
|
||||
{{ $t('Settings.MiscellaneousTab.UnableToLoadLight') }}
|
||||
</p>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</template>
|
||||
</v-card-text>
|
||||
<v-card-actions class="d-flex justify-end">
|
||||
<v-btn text @click="$emit('close')">{{ $t('Settings.Close') }}</v-btn>
|
||||
<v-btn text color="primary" @click="createGroup">
|
||||
{{ $t('Settings.MiscellaneousTab.AddGroup') }}
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Mixins, Prop } from 'vue-property-decorator'
|
||||
import BaseMixin from '../mixins/base'
|
||||
import SettingsRow from '@/components/settings/SettingsRow.vue'
|
||||
import { mdiDelete, mdiPalette, mdiPencil } from '@mdi/js'
|
||||
import { caseInsensitiveSort, convertName } from '@/plugins/helpers'
|
||||
import { PrinterStateLight } from '@/store/printer/types'
|
||||
import { GuiMacrosStateMacrogroup } from '@/store/gui/macros/types'
|
||||
import { GuiMiscellaneousStateEntry, GuiMiscellaneousStateEntryLightgroup } from '@/store/gui/miscellaneous/types'
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
SettingsRow,
|
||||
},
|
||||
})
|
||||
export default class SettingsMiscellaneousTabLightGroups extends Mixins(BaseMixin) {
|
||||
mdiDelete = mdiDelete
|
||||
mdiPalette = mdiPalette
|
||||
mdiPencil = mdiPencil
|
||||
|
||||
convertName = convertName
|
||||
|
||||
private boolForm = false
|
||||
|
||||
private form: {
|
||||
id: string | null
|
||||
name: string
|
||||
start: number
|
||||
end: number
|
||||
} = {
|
||||
id: null,
|
||||
name: '',
|
||||
start: 1,
|
||||
end: 1,
|
||||
}
|
||||
|
||||
private rules = {
|
||||
required: (value: string) => value !== '' || 'required',
|
||||
groupUnique: (value: string) => !this.existsGroupName(value) || 'Name already exists',
|
||||
minStart: (value: number) => value > 0 || 'smaller than 1',
|
||||
minEnd: (value: number) => value >= this.form.start || 'smaller than start value',
|
||||
max: (value: number) => value <= (this.light?.chainCount ?? 1) || 'higher than chain_count',
|
||||
}
|
||||
|
||||
@Prop({ type: Object, default: null })
|
||||
declare light: PrinterStateLight | null
|
||||
|
||||
get entry() {
|
||||
return this.$store.getters['gui/miscellaneous/getEntry']({
|
||||
type: this.light?.type,
|
||||
name: this.light?.name,
|
||||
}) as GuiMiscellaneousStateEntry | null
|
||||
}
|
||||
|
||||
get groups() {
|
||||
if (!this.entry) return []
|
||||
|
||||
const groups: GuiMiscellaneousStateEntryLightgroup[] = []
|
||||
Object.entries(this.entry.lightgroups).forEach(([key, lightgroup]) => {
|
||||
groups.push({
|
||||
name: lightgroup.name,
|
||||
start: lightgroup.start,
|
||||
end: lightgroup.end,
|
||||
id: key,
|
||||
})
|
||||
})
|
||||
window.console.log('getEntryLightgroups', groups)
|
||||
|
||||
return caseInsensitiveSort(groups, 'name')
|
||||
}
|
||||
|
||||
createGroup() {
|
||||
this.form.id = null
|
||||
this.form.name = ''
|
||||
this.form.start = 1
|
||||
this.form.end = this.light?.chainCount ?? 1
|
||||
this.boolForm = true
|
||||
}
|
||||
|
||||
editGroup(group: GuiMiscellaneousStateEntryLightgroup) {
|
||||
this.form.id = group.id ?? null
|
||||
this.form.name = group.name
|
||||
this.form.start = group.start
|
||||
this.form.end = group.end
|
||||
this.boolForm = true
|
||||
}
|
||||
|
||||
closeForm() {
|
||||
this.boolForm = false
|
||||
}
|
||||
|
||||
storeGroup() {
|
||||
this.$store.dispatch('gui/miscellaneous/storeLightgroup', {
|
||||
entry: this.light,
|
||||
lightgroup: this.form,
|
||||
})
|
||||
|
||||
this.boolForm = false
|
||||
}
|
||||
|
||||
updateGroup() {
|
||||
this.$store.dispatch('gui/miscellaneous/updateLightgroup', {
|
||||
entry: this.light,
|
||||
lightgroup: this.form,
|
||||
})
|
||||
|
||||
this.boolForm = false
|
||||
}
|
||||
|
||||
deleteGroup(groupId: string) {
|
||||
this.$store.dispatch('gui/miscellaneous/deleteLightgroup', {
|
||||
entry: this.light,
|
||||
lightgroupId: groupId,
|
||||
})
|
||||
}
|
||||
|
||||
existsGroupName(name: string) {
|
||||
return (
|
||||
this.groups.findIndex(
|
||||
(group: GuiMacrosStateMacrogroup) => group.name === name && group.id != this.form.id
|
||||
) >= 0
|
||||
)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
467
src/components/settings/SettingsMiscellaneousTabLightPresets.vue
Normal file
467
src/components/settings/SettingsMiscellaneousTabLightPresets.vue
Normal file
@ -0,0 +1,467 @@
|
||||
<template>
|
||||
<div>
|
||||
<template v-if="boolForm">
|
||||
<v-card-text>
|
||||
<h3 class="text-h5 mb-3">{{ $t('Settings.MiscellaneousTab.CreatePreset') }}</h3>
|
||||
<settings-row :title="$t('Settings.MiscellaneousTab.Name').toString()">
|
||||
<v-text-field
|
||||
v-model="form.name"
|
||||
hide-details="auto"
|
||||
:rules="[rules.required, rules.presetUnique]"
|
||||
dense
|
||||
outlined></v-text-field>
|
||||
</settings-row>
|
||||
<v-divider class="my-2"></v-divider>
|
||||
<settings-row :title="$t('Settings.MiscellaneousTab.Color').toString()">
|
||||
<v-row>
|
||||
<v-col class="text-center">
|
||||
<color-picker
|
||||
:color="colorRGB"
|
||||
:options="colorPickerOptions"
|
||||
@update:color="onColorRGBChanged" />
|
||||
<color-picker
|
||||
v-if="existWhite"
|
||||
:color="colorRGBW"
|
||||
:options="colorPickerWhiteOptions"
|
||||
class="mt-3"
|
||||
@update:color="onColorWhiteChanged" />
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-row v-if="existRed">
|
||||
<v-col>
|
||||
<number-input
|
||||
:label="$t('Panels.MiscellaneousPanel.Light.Red')"
|
||||
param="red"
|
||||
:target="redInt"
|
||||
:min="0"
|
||||
:max="255"
|
||||
:dec="1"
|
||||
:step="1"
|
||||
:output-error-msg="true"
|
||||
:has-spinner="true"
|
||||
@submit="onColorInput" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-if="existGreen">
|
||||
<v-col>
|
||||
<number-input
|
||||
:label="$t('Panels.MiscellaneousPanel.Light.Green')"
|
||||
param="green"
|
||||
:target="greenInt"
|
||||
:min="0"
|
||||
:max="255"
|
||||
:dec="1"
|
||||
:step="1"
|
||||
:has-spinner="true"
|
||||
@submit="onColorInput" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-if="existBlue">
|
||||
<v-col>
|
||||
<number-input
|
||||
:label="$t('Panels.MiscellaneousPanel.Light.Blue')"
|
||||
param="blue"
|
||||
:target="blueInt"
|
||||
:min="0"
|
||||
:max="255"
|
||||
:dec="1"
|
||||
:step="1"
|
||||
:has-spinner="true"
|
||||
@submit="onColorInput" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-if="existWhite">
|
||||
<v-col>
|
||||
<number-input
|
||||
:label="$t('Panels.MiscellaneousPanel.Light.White')"
|
||||
param="white"
|
||||
:target="whiteInt"
|
||||
:min="0"
|
||||
:max="255"
|
||||
:dec="1"
|
||||
:step="1"
|
||||
:has-spinner="true"
|
||||
@submit="onColorInput" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</settings-row>
|
||||
</v-card-text>
|
||||
<v-card-actions class="d-flex justify-end">
|
||||
<v-btn text @click="closeForm">{{ $t('Settings.Cancel') }}</v-btn>
|
||||
<v-btn v-if="form.id !== null" text color="primary" @click="updatePreset">
|
||||
{{ $t('Settings.Update') }}
|
||||
</v-btn>
|
||||
<v-btn v-else text color="primary" @click="storePreset">{{ $t('Settings.Store') }}</v-btn>
|
||||
</v-card-actions>
|
||||
</template>
|
||||
<template v-else>
|
||||
<v-card-text>
|
||||
<h3 class="text-h5 mb-3">{{ $t('Settings.MiscellaneousTab.LightPresets', { name: light.name }) }}</h3>
|
||||
<template v-if="light">
|
||||
<template v-if="presets.length">
|
||||
<div v-for="(preset, index) in presets" :key="preset.id">
|
||||
<v-divider v-if="index" class="my-2"></v-divider>
|
||||
<settings-row
|
||||
:title="preset.name"
|
||||
:sub-title="entryDescriptionText(preset)"
|
||||
:dynamic-slot-width="true">
|
||||
<v-btn small outlined class="ml-3" @click="editPreset(preset)">
|
||||
<v-icon left small>{{ mdiPencil }}</v-icon>
|
||||
{{ $t('Settings.Edit') }}
|
||||
</v-btn>
|
||||
<v-btn
|
||||
small
|
||||
outlined
|
||||
class="ml-3 minwidth-0 px-2"
|
||||
color="error"
|
||||
@click="deletePreset(preset.id)">
|
||||
<v-icon small>{{ mdiDelete }}</v-icon>
|
||||
</v-btn>
|
||||
</settings-row>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<p class="mb-0 text-center font-italic">
|
||||
{{ $t('Settings.MiscellaneousTab.NoPresetFound') }}
|
||||
</p>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</template>
|
||||
</template>
|
||||
<template v-else>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<p class="mb-0 text-center font-italic">
|
||||
{{ $t('Settings.MiscellaneousTab.UnableToLoadPreset') }}
|
||||
</p>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</template>
|
||||
</v-card-text>
|
||||
<v-card-actions class="d-flex justify-end">
|
||||
<v-btn text @click="$emit('close')">{{ $t('Settings.Close') }}</v-btn>
|
||||
<v-btn text color="primary" @click="createPreset">
|
||||
{{ $t('Settings.MiscellaneousTab.AddPreset') }}
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Mixins, Prop } from 'vue-property-decorator'
|
||||
import BaseMixin from '../mixins/base'
|
||||
import SettingsRow from '@/components/settings/SettingsRow.vue'
|
||||
import { mdiDelete, mdiPencil } from '@mdi/js'
|
||||
import { caseInsensitiveSort, convertName } from '@/plugins/helpers'
|
||||
import { PrinterStateLight } from '@/store/printer/types'
|
||||
import { GuiMiscellaneousStateEntry, GuiMiscellaneousStateEntryPreset } from '@/store/gui/miscellaneous/types'
|
||||
import { ColorPickerProps } from '@jaames/iro/dist/ColorPicker.d'
|
||||
import iro from '@jaames/iro'
|
||||
import { Debounce } from 'vue-debounce-decorator'
|
||||
import { IroColor } from '@irojs/iro-core'
|
||||
|
||||
interface ColorData {
|
||||
red: number | null
|
||||
green: number | null
|
||||
blue: number | null
|
||||
white: number | null
|
||||
}
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
SettingsRow,
|
||||
},
|
||||
})
|
||||
export default class SettingsMiscellaneousTabLightPresets extends Mixins(BaseMixin) {
|
||||
mdiDelete = mdiDelete
|
||||
mdiPencil = mdiPencil
|
||||
|
||||
convertName = convertName
|
||||
|
||||
private boolForm = false
|
||||
|
||||
private form: {
|
||||
id: string | null
|
||||
name: string
|
||||
red: number | null
|
||||
green: number | null
|
||||
blue: number | null
|
||||
white: number | null
|
||||
} = {
|
||||
id: null,
|
||||
name: '',
|
||||
red: null,
|
||||
green: null,
|
||||
blue: null,
|
||||
white: null,
|
||||
}
|
||||
|
||||
private rules = {
|
||||
required: (value: string) => value !== '' || 'required',
|
||||
presetUnique: (value: string) => !this.existsPresetName(value) || 'Name already exists',
|
||||
min: (value: number) => value >= 0 || 'Must be minimum 0',
|
||||
max: (value: number) => value <= 255 || 'Must be smaller then 256',
|
||||
}
|
||||
|
||||
@Prop({ type: Object, default: null })
|
||||
declare light: PrinterStateLight | null
|
||||
|
||||
get entry() {
|
||||
return this.$store.getters['gui/miscellaneous/getEntry']({
|
||||
type: this.light?.type,
|
||||
name: this.light?.name,
|
||||
}) as GuiMiscellaneousStateEntry | null
|
||||
}
|
||||
|
||||
get presets() {
|
||||
if (!this.entry) return []
|
||||
|
||||
const presets: GuiMiscellaneousStateEntryPreset[] = []
|
||||
Object.entries(this.entry.presets).forEach(([key, preset]) => {
|
||||
presets.push({
|
||||
...preset,
|
||||
id: key,
|
||||
})
|
||||
})
|
||||
window.console.log('getEntryPresets', presets)
|
||||
|
||||
return caseInsensitiveSort(presets, 'name')
|
||||
}
|
||||
|
||||
get existRed() {
|
||||
return this.light?.colorOrder.indexOf('R') !== -1
|
||||
}
|
||||
|
||||
get existGreen() {
|
||||
return this.light?.colorOrder.indexOf('G') !== -1
|
||||
}
|
||||
|
||||
get existBlue() {
|
||||
return this.light?.colorOrder.indexOf('B') !== -1
|
||||
}
|
||||
|
||||
get existWhite() {
|
||||
return this.light?.colorOrder.indexOf('W') !== -1
|
||||
}
|
||||
|
||||
get colorRGB() {
|
||||
return `rgb(${Math.round(this.form.red ?? 0)}, ${Math.round(this.form.green ?? 0)}, ${Math.round(
|
||||
this.form.blue ?? 0
|
||||
)})`
|
||||
}
|
||||
|
||||
get colorRGBW() {
|
||||
return `rgba(255, 255, 255, ${(this.form.white ?? 0) / 255})`
|
||||
}
|
||||
|
||||
get redInt() {
|
||||
return Math.round(this.form.red ?? 0)
|
||||
}
|
||||
|
||||
get greenInt() {
|
||||
return Math.round(this.form.green ?? 0)
|
||||
}
|
||||
|
||||
get blueInt() {
|
||||
return Math.round(this.form.blue ?? 0)
|
||||
}
|
||||
|
||||
get whiteInt() {
|
||||
return Math.round(this.form.white ?? 0)
|
||||
}
|
||||
|
||||
get colorPickerOptions() {
|
||||
let options: ColorPickerProps = {
|
||||
width: 200,
|
||||
margin: 15,
|
||||
layout: [],
|
||||
}
|
||||
|
||||
if (this.existRed) {
|
||||
// @ts-ignore
|
||||
options?.layout.push({
|
||||
component: iro.ui.Slider,
|
||||
options: {
|
||||
sliderType: 'red',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if (this.existGreen) {
|
||||
// @ts-ignore
|
||||
options?.layout.push({
|
||||
component: iro.ui.Slider,
|
||||
options: {
|
||||
sliderType: 'green',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if (this.existBlue) {
|
||||
// @ts-ignore
|
||||
options?.layout.push({
|
||||
component: iro.ui.Slider,
|
||||
options: {
|
||||
sliderType: 'blue',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if (this.existRed && this.existGreen && this.existBlue) {
|
||||
options.layout = [
|
||||
{
|
||||
component: iro.ui.Wheel,
|
||||
},
|
||||
{
|
||||
component: iro.ui.Slider,
|
||||
options: {
|
||||
sliderType: 'value',
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
get colorPickerWhiteOptions() {
|
||||
let options: ColorPickerProps = {
|
||||
width: 200,
|
||||
margin: 15,
|
||||
layout: [
|
||||
{
|
||||
component: iro.ui.Slider,
|
||||
options: {
|
||||
sliderType: 'alpha',
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
entryDescriptionText(preset: GuiMiscellaneousStateEntryPreset) {
|
||||
let output: string[] = []
|
||||
|
||||
if (this.light?.colorOrder.includes('R')) output.push(`R: ${preset.red}`)
|
||||
if (this.light?.colorOrder.includes('G')) output.push(`G: ${preset.green}`)
|
||||
if (this.light?.colorOrder.includes('B')) output.push(`B: ${preset.blue}`)
|
||||
if (this.light?.colorOrder.includes('W')) output.push(`W: ${preset.white}`)
|
||||
|
||||
return output.join(', ')
|
||||
}
|
||||
|
||||
createPreset() {
|
||||
this.form.id = null
|
||||
this.form.name = ''
|
||||
this.form.red = this.light?.colorOrder.indexOf('R') != -1 ? 0 : null
|
||||
this.form.green = this.light?.colorOrder.indexOf('G') != -1 ? 0 : null
|
||||
this.form.blue = this.light?.colorOrder.indexOf('B') != -1 ? 0 : null
|
||||
this.form.white = this.light?.colorOrder.indexOf('W') != -1 ? 0 : null
|
||||
this.boolForm = true
|
||||
}
|
||||
|
||||
editPreset(preset: GuiMiscellaneousStateEntryPreset) {
|
||||
this.form.id = preset.id ?? null
|
||||
this.form.name = preset.name
|
||||
this.form.red = this.light?.colorOrder.indexOf('R') != -1 ? preset.red : null
|
||||
this.form.green = this.light?.colorOrder.indexOf('G') != -1 ? preset.green : null
|
||||
this.form.blue = this.light?.colorOrder.indexOf('B') != -1 ? preset.blue : null
|
||||
this.form.white = this.light?.colorOrder.indexOf('W') != -1 ? preset.white : null
|
||||
this.boolForm = true
|
||||
}
|
||||
|
||||
closeForm() {
|
||||
this.boolForm = false
|
||||
}
|
||||
|
||||
storePreset() {
|
||||
this.$store.dispatch('gui/miscellaneous/storePreset', {
|
||||
entry: this.light,
|
||||
preset: this.form,
|
||||
})
|
||||
|
||||
this.boolForm = false
|
||||
}
|
||||
|
||||
updatePreset() {
|
||||
this.$store.dispatch('gui/miscellaneous/updatePreset', {
|
||||
entry: this.light,
|
||||
preset: this.form,
|
||||
})
|
||||
|
||||
this.boolForm = false
|
||||
}
|
||||
|
||||
deletePreset(presetId: string) {
|
||||
this.$store.dispatch('gui/miscellaneous/deletePreset', {
|
||||
entry: this.light,
|
||||
presetId: presetId,
|
||||
})
|
||||
}
|
||||
|
||||
existsPresetName(name: string) {
|
||||
return (
|
||||
this.presets.findIndex(
|
||||
(group: GuiMiscellaneousStateEntryPreset) => group.name === name && group.id != this.form.id
|
||||
) >= 0
|
||||
)
|
||||
}
|
||||
|
||||
@Debounce({ time: 250 })
|
||||
onColorRGBChanged(payload: IroColor) {
|
||||
const color: ColorData = {
|
||||
red: payload.red,
|
||||
green: payload.green,
|
||||
blue: payload.blue,
|
||||
white: this.form.white,
|
||||
}
|
||||
|
||||
this.colorChanged(color)
|
||||
}
|
||||
|
||||
@Debounce({ time: 250 })
|
||||
onColorWhiteChanged(payload: IroColor) {
|
||||
const color: ColorData = {
|
||||
red: this.form.red,
|
||||
green: this.form.green,
|
||||
blue: this.form.blue,
|
||||
white: this.form.white,
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
color.white = payload.alpha * 255
|
||||
|
||||
this.colorChanged(color)
|
||||
}
|
||||
|
||||
onColorInput(payload: { name: string; value: number }) {
|
||||
const color: ColorData = {
|
||||
red: this.form.red,
|
||||
green: this.form.green,
|
||||
blue: this.form.blue,
|
||||
white: this.form.white,
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
color[payload.name] = payload.value
|
||||
|
||||
this.colorChanged(color)
|
||||
}
|
||||
|
||||
colorChanged(color: ColorData) {
|
||||
this.form.red = color.red
|
||||
this.form.green = color.green
|
||||
this.form.blue = color.blue
|
||||
this.form.white = color.white
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
@ -948,7 +948,6 @@
|
||||
"EditWebcam": "Rediger Webcam",
|
||||
"FlipWebcam": "Vend webcam-billedet:",
|
||||
"Horizontally": "horisontalt",
|
||||
"Vertically": "vertikalt",
|
||||
"IconBed": "Bed",
|
||||
"IconCam": "Kamera",
|
||||
"IconDoor": "Dør",
|
||||
@ -971,6 +970,7 @@
|
||||
"UrlSnapshot": "URL Snapshot",
|
||||
"UrlStream": "URL Stream",
|
||||
"Uv4lMjpeg": "UV4L-MJPEG",
|
||||
"Vertically": "vertikalt",
|
||||
"Webcams": "Webcams"
|
||||
}
|
||||
},
|
||||
|
@ -530,6 +530,12 @@
|
||||
},
|
||||
"MiscellaneousPanel": {
|
||||
"Headline": "Sonstiges",
|
||||
"Light": {
|
||||
"Blue": "blau",
|
||||
"Green": "grün",
|
||||
"Red": "rot",
|
||||
"White": "weiß"
|
||||
},
|
||||
"RunoutSensor": {
|
||||
"Detected": "erkannt",
|
||||
"Disabled": "deaktiviert",
|
||||
@ -833,6 +839,30 @@
|
||||
"UnknownGroup": "Unbekannte Gruppe",
|
||||
"Warning": "Warnung"
|
||||
},
|
||||
"MiscellaneousTab": {
|
||||
"AddGroup": "Gruppe hinzufügen",
|
||||
"AddPreset": "Voreinstellung hinzufügen",
|
||||
"Color": "Farbe",
|
||||
"CreateGroup": "Gruppe erstellen",
|
||||
"CreatePreset": "Voreinstellung erstellen",
|
||||
"End": "Ende",
|
||||
"EndDescription": "Letzte LED von dieser Gruppe.",
|
||||
"Groups": "Gruppen",
|
||||
"GroupSubTitle": "Start: {start}, Ende: {end}",
|
||||
"LightGroups": "{name} - Gruppen",
|
||||
"LightPresets": "{name} - Voreinstellungen",
|
||||
"Miscellaneous": "Sonstiges",
|
||||
"Name": "Name",
|
||||
"NoDevicesFound": "Keine Komponente gefunden",
|
||||
"NoGroupFound": "Keine Gruppe gefunden",
|
||||
"NoPresetFound": "Keine Voreinstellungen gefunden",
|
||||
"Presets": "Voreinstellungen",
|
||||
"PresetSubTitle": "R: {red}, G: {green}, B: {blue}, W: {white}",
|
||||
"Start": "Start",
|
||||
"StartDescription": "Erste LED von dieser Gruppe.",
|
||||
"UnableToLoadLight": "Licht konnte nicht geladen werden",
|
||||
"UnableToLoadPreset": "Voreinstellung konnte nicht geladen werden"
|
||||
},
|
||||
"PresetsTab": {
|
||||
"AddPreset": "Preset hinzufügen",
|
||||
"Cooldown": "Abkühlen",
|
||||
@ -860,6 +890,7 @@
|
||||
"UpdatePrinter": "Drucker aktualisieren",
|
||||
"UseConfigJson": "InstanceDB = JSON erkannt. Bitte bearbeite die config.json um die Druckerliste zu modifizieren."
|
||||
},
|
||||
"Store": "anlegen",
|
||||
"TimelapseTab": {
|
||||
"Autorender": "Autorender",
|
||||
"AutorenderDescription": "Wenn diese Option aktiviert ist, wird das Zeitraffervideo am Ende des Druckvorgangs automatisch gerendert",
|
||||
@ -954,6 +985,7 @@
|
||||
"ShowWebcamInNavigation": "Zeige Webcam in der Navigation",
|
||||
"UiSettings": "UI-Einstellungen"
|
||||
},
|
||||
"Update": "speichern",
|
||||
"WebcamsTab": {
|
||||
"AddWebcam": "Webcam hinzufügen",
|
||||
"CreateWebcam": "Erstelle Webcam",
|
||||
@ -961,7 +993,6 @@
|
||||
"EditWebcam": "Webcam bearbeiten",
|
||||
"FlipWebcam": "Webcam-Bild spiegeln:",
|
||||
"Horizontally": "horizontal",
|
||||
"Vertically": "vertikal",
|
||||
"IconBed": "Bett",
|
||||
"IconCam": "Kamera",
|
||||
"IconDoor": "Tür",
|
||||
@ -984,6 +1015,7 @@
|
||||
"UrlSnapshot": "Schnappschuss URL",
|
||||
"UrlStream": "Stream URL",
|
||||
"Uv4lMjpeg": "UV4L-MJPEG",
|
||||
"Vertically": "vertikal",
|
||||
"Webcams": "Webcams"
|
||||
}
|
||||
},
|
||||
|
@ -530,6 +530,12 @@
|
||||
},
|
||||
"MiscellaneousPanel": {
|
||||
"Headline": "Miscellaneous",
|
||||
"Light": {
|
||||
"Blue": "blue",
|
||||
"Green": "green",
|
||||
"Red": "red",
|
||||
"White": "white"
|
||||
},
|
||||
"RunoutSensor": {
|
||||
"Detected": "detected",
|
||||
"Disabled": "disabled",
|
||||
@ -837,6 +843,30 @@
|
||||
"UnknownGroup": "Unknown Group",
|
||||
"Warning": "warning"
|
||||
},
|
||||
"MiscellaneousTab": {
|
||||
"AddGroup": "add group",
|
||||
"AddPreset": "add preset",
|
||||
"Color": "Color",
|
||||
"CreateGroup": "Create group",
|
||||
"CreatePreset": "Create preset",
|
||||
"End": "End",
|
||||
"EndDescription": "Last LED of this group.",
|
||||
"Groups": "Groups",
|
||||
"GroupSubTitle": "Start: {start}, End: {end}",
|
||||
"LightGroups": "{name} - Groups",
|
||||
"LightPresets": "{name} - Presets",
|
||||
"Miscellaneous": "Miscellaneous",
|
||||
"Name": "Name",
|
||||
"NoDevicesFound": "No devices found",
|
||||
"NoGroupFound": "No group found",
|
||||
"NoPresetFound": "No preset found",
|
||||
"Presets": "Presets",
|
||||
"PresetSubTitle": "R: {red}, G: {green}, B: {blue}, W: {white}",
|
||||
"Start": "Start",
|
||||
"StartDescription": "First LED of this group.",
|
||||
"UnableToLoadLight": "Unable to load light",
|
||||
"UnableToLoadPreset": "Unable to load preset"
|
||||
},
|
||||
"PresetsTab": {
|
||||
"AddPreset": "add preset",
|
||||
"Cooldown": "Cooldown",
|
||||
@ -864,6 +894,7 @@
|
||||
"UpdatePrinter": "Update Printer",
|
||||
"UseConfigJson": "InstanceDB = JSON detected. Please use the config.json to modify the printers list."
|
||||
},
|
||||
"Store": "store",
|
||||
"TimelapseTab": {
|
||||
"Autorender": "Autorender",
|
||||
"AutorenderDescription": "If enabled, the timelapse video will automatically render at the end of the print",
|
||||
@ -958,6 +989,7 @@
|
||||
"ShowWebcamInNavigation": "Show Webcam in navigation",
|
||||
"UiSettings": "UI-Settings"
|
||||
},
|
||||
"Update": "update",
|
||||
"WebcamsTab": {
|
||||
"AddWebcam": "add webcam",
|
||||
"CreateWebcam": "Create Webcam",
|
||||
@ -965,7 +997,6 @@
|
||||
"EditWebcam": "Edit Webcam",
|
||||
"FlipWebcam": "Flip webcam image:",
|
||||
"Horizontally": "horizontally",
|
||||
"Vertically": "vertically",
|
||||
"IconBed": "Bed",
|
||||
"IconCam": "Cam",
|
||||
"IconDoor": "Door",
|
||||
@ -988,6 +1019,7 @@
|
||||
"UrlSnapshot": "URL Snapshot",
|
||||
"UrlStream": "URL Stream",
|
||||
"Uv4lMjpeg": "UV4L-MJPEG",
|
||||
"Vertically": "vertically",
|
||||
"Webcams": "Webcams"
|
||||
}
|
||||
},
|
||||
|
@ -929,7 +929,6 @@
|
||||
"EditWebcam": "Editar cámara web",
|
||||
"FlipWebcam": "Voltear la imagen de la cámara web:",
|
||||
"Horizontally": "horizontalmente",
|
||||
"Vertically": "verticalmente",
|
||||
"IconBed": "Cama",
|
||||
"IconCam": "Cámara",
|
||||
"IconDoor": "Puerta",
|
||||
@ -951,6 +950,7 @@
|
||||
"UrlSnapshot": "URL Snapshot",
|
||||
"UrlStream": "URL Stream",
|
||||
"Uv4lMjpeg": "UV4L-MJPEG",
|
||||
"Vertically": "verticalmente",
|
||||
"Webcams": "Cámaras web"
|
||||
}
|
||||
},
|
||||
|
@ -953,7 +953,6 @@
|
||||
"EditWebcam": "Editer caméra",
|
||||
"FlipWebcam": "Miroir l'image de la webcam:",
|
||||
"Horizontally": "horizontal",
|
||||
"Vertically": "vertical",
|
||||
"IconBed": "Plateau",
|
||||
"IconCam": "Caméra",
|
||||
"IconDoor": "Porte",
|
||||
@ -976,6 +975,7 @@
|
||||
"UrlSnapshot": "URL des instantanés",
|
||||
"UrlStream": "URL du flux",
|
||||
"Uv4lMjpeg": "UV4L-MJPEG",
|
||||
"Vertically": "vertical",
|
||||
"Webcams": "Caméras"
|
||||
}
|
||||
},
|
||||
|
@ -929,7 +929,6 @@
|
||||
"EditWebcam": "Webkamera szerkesztése",
|
||||
"FlipWebcam": "Webkamera tükrözése:",
|
||||
"Horizontally": "vízszintes",
|
||||
"Vertically": "függőleges",
|
||||
"IconBed": "Asztal",
|
||||
"IconCam": "Kamera",
|
||||
"IconDoor": "Ajtó",
|
||||
@ -951,6 +950,7 @@
|
||||
"UrlSnapshot": "Snapshot URL-je ",
|
||||
"UrlStream": "Stream URL-je ",
|
||||
"Uv4lMjpeg": "UV4L-MJPEG",
|
||||
"Vertically": "függőleges",
|
||||
"Webcams": "Webkamerák"
|
||||
}
|
||||
},
|
||||
|
@ -796,7 +796,6 @@
|
||||
"EditWebcam": "Modifica Webcam",
|
||||
"FlipWebcam": "Specchio dell'immagine della webcam:",
|
||||
"Horizontally": "orizzontalmente",
|
||||
"Vertically": "verticalmente",
|
||||
"IconBed": "Letto",
|
||||
"IconCam": "Cam",
|
||||
"IconDoor": "Porta",
|
||||
@ -818,6 +817,7 @@
|
||||
"UrlSnapshot": "URL Snaphot",
|
||||
"UrlStream": "URL Stream",
|
||||
"Uv4lMjpeg": "UV4L-MJPEG",
|
||||
"Vertically": "verticalmente",
|
||||
"Webcams": "Webcam"
|
||||
}
|
||||
},
|
||||
|
@ -952,7 +952,6 @@
|
||||
"EditWebcam": "Bewerk Webcam",
|
||||
"FlipWebcam": "Flip webcam beeld:",
|
||||
"Horizontally": "horizontaal",
|
||||
"Vertically": "verticaal",
|
||||
"IconBed": "Bed",
|
||||
"IconCam": "Camera",
|
||||
"IconDoor": "Deur",
|
||||
@ -975,6 +974,7 @@
|
||||
"UrlSnapshot": "URL Snapshot",
|
||||
"UrlStream": "URL Stream",
|
||||
"Uv4lMjpeg": "UV4L-MJPEG",
|
||||
"Vertically": "verticaal",
|
||||
"Webcams": "Webcams"
|
||||
}
|
||||
},
|
||||
|
@ -930,7 +930,6 @@
|
||||
"EditWebcam": "Edytuj kamerę",
|
||||
"FlipWebcam": "Odwróć widok kamery:",
|
||||
"Horizontally": "poziomo",
|
||||
"Vertically": "pionowo",
|
||||
"IconBed": "Stół",
|
||||
"IconCam": "Kamera",
|
||||
"IconDoor": "Drzwi",
|
||||
@ -952,6 +951,7 @@
|
||||
"UrlSnapshot": "Migawka URL",
|
||||
"UrlStream": "Transmisja po URL",
|
||||
"Uv4lMjpeg": "UV4L-MJPEG",
|
||||
"Vertically": "pionowo",
|
||||
"Webcams": "Kamery"
|
||||
}
|
||||
},
|
||||
|
@ -930,7 +930,6 @@
|
||||
"EditWebcam": "Редактирование веб-камеры",
|
||||
"FlipWebcam": "Зеркальное отображение веб-камеры:",
|
||||
"Horizontally": "горизонтально",
|
||||
"Vertically": "вертикально",
|
||||
"IconBed": "Кровать",
|
||||
"IconCam": "Камера",
|
||||
"IconDoor": "Дверь",
|
||||
@ -952,6 +951,7 @@
|
||||
"UrlSnapshot": "URL моментального снимка",
|
||||
"UrlStream": "URL потока",
|
||||
"Uv4lMjpeg": "UV4L-MJPEG",
|
||||
"Vertically": "вертикально",
|
||||
"Webcams": "Веб-камеры"
|
||||
}
|
||||
},
|
||||
|
@ -824,7 +824,6 @@
|
||||
"EditWebcam": "Redigera webbkamera",
|
||||
"FlipWebcam": "Vänd webbkamerabilden",
|
||||
"Horizontally": "horisontellt",
|
||||
"Vertically": "vertikalt",
|
||||
"IconBed": "Bädd",
|
||||
"IconCam": "Kamera",
|
||||
"IconDoor": "Dörr",
|
||||
@ -846,6 +845,7 @@
|
||||
"UrlSnapshot": "Ögonblicksbild av webbadressen",
|
||||
"UrlStream": "Webbadressström",
|
||||
"Uv4lMjpeg": "UV4L-MJPEG",
|
||||
"Vertically": "vertikalt",
|
||||
"Webcams": "Webbkameror"
|
||||
}
|
||||
},
|
||||
|
@ -951,7 +951,6 @@
|
||||
"EditWebcam": "Web Kamerası Düzenle",
|
||||
"FlipWebcam": "Web kamerasını çevirin:",
|
||||
"Horizontally": "yatay olarak",
|
||||
"Vertically": "dikey olarak",
|
||||
"IconBed": "Yatak",
|
||||
"IconCam": "Kamera",
|
||||
"IconDoor": "Kapı",
|
||||
@ -974,6 +973,7 @@
|
||||
"UrlSnapshot": "URL Anlık Görüntüsü",
|
||||
"UrlStream": "URL Akışı",
|
||||
"Uv4lMjpeg": "UV4L-MJPEG",
|
||||
"Vertically": "dikey olarak",
|
||||
"Webcams": "Web kameraları"
|
||||
}
|
||||
},
|
||||
|
@ -952,7 +952,6 @@
|
||||
"EditWebcam": "Редагувати веб-камеру",
|
||||
"FlipWebcam": "Повернути веб-камеру:",
|
||||
"Horizontally": "горизонтально",
|
||||
"Vertically": "вертикально",
|
||||
"IconBed": "Ліжко",
|
||||
"IconCam": "Cam",
|
||||
"IconDoor": "Двері",
|
||||
@ -975,6 +974,7 @@
|
||||
"UrlSnapshot": "URL-адреса Знімку",
|
||||
"UrlStream": "URL-адреса Потоку",
|
||||
"Uv4lMjpeg": "UV4L-MJPEG",
|
||||
"Vertically": "вертикально",
|
||||
"Webcams": "Веб-камери"
|
||||
}
|
||||
},
|
||||
|
@ -9,6 +9,7 @@ import { defaultLogoColor, defaultPrimaryColor } from '@/store/variables'
|
||||
import { console } from '@/store/gui/console'
|
||||
import { gcodehistory } from '@/store/gui/gcodehistory'
|
||||
import { macros } from '@/store/gui/macros'
|
||||
import { miscellaneous } from '@/store/gui/miscellaneous'
|
||||
import { presets } from '@/store/gui/presets'
|
||||
import { remoteprinters } from '@/store/gui/remoteprinters'
|
||||
import { webcams } from '@/store/gui/webcams'
|
||||
@ -255,6 +256,7 @@ export const gui: Module<GuiState, any> = {
|
||||
console,
|
||||
gcodehistory,
|
||||
macros,
|
||||
miscellaneous,
|
||||
notifications,
|
||||
presets,
|
||||
remoteprinters,
|
||||
|
133
src/store/gui/miscellaneous/actions.ts
Normal file
133
src/store/gui/miscellaneous/actions.ts
Normal file
@ -0,0 +1,133 @@
|
||||
import { ActionTree } from 'vuex'
|
||||
import { RootState } from '@/store/types'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import Vue from 'vue'
|
||||
import {
|
||||
GuiMiscellaneousState,
|
||||
GuiMiscellaneousStateEntryLightgroup,
|
||||
GuiMiscellaneousStateEntryPreset,
|
||||
} from '@/store/gui/miscellaneous/types'
|
||||
|
||||
export const actions: ActionTree<GuiMiscellaneousState, RootState> = {
|
||||
reset({ commit }) {
|
||||
commit('reset')
|
||||
},
|
||||
|
||||
upload({ state }, id) {
|
||||
Vue.$socket.emit('server.database.post_item', {
|
||||
namespace: 'mainsail',
|
||||
key: 'miscellaneous.entries.' + id,
|
||||
value: state.entries[id],
|
||||
})
|
||||
},
|
||||
|
||||
async store({ commit, dispatch }, payload: payloadStore) {
|
||||
const id = uuidv4()
|
||||
|
||||
await commit('store', { id, values: payload })
|
||||
await dispatch('upload', id)
|
||||
|
||||
return id
|
||||
},
|
||||
|
||||
async storeLightgroup({ commit, dispatch, getters }, payload: payloadStoreLightgroup) {
|
||||
let entryId = getters['getId'](payload.entry)
|
||||
if (entryId === null) entryId = await dispatch('store', payload.entry)
|
||||
|
||||
const lightgroupId = uuidv4()
|
||||
await commit('updateLightgroup', { entryId, lightgroupId, values: payload.lightgroup })
|
||||
|
||||
await dispatch('upload', entryId)
|
||||
|
||||
return lightgroupId
|
||||
},
|
||||
|
||||
async updateLightgroup({ commit, dispatch, getters }, payload: payloadStoreLightgroup) {
|
||||
const entryId = getters['getId'](payload.entry)
|
||||
if (entryId === null) return
|
||||
|
||||
await commit('updateLightgroup', { entryId, lightgroupId: payload.lightgroup.id, values: payload.lightgroup })
|
||||
|
||||
await dispatch('upload', entryId)
|
||||
|
||||
return payload.lightgroup.id
|
||||
},
|
||||
|
||||
async deleteLightgroup({ commit, dispatch, getters }, payload: payloadDeleteLightgroup) {
|
||||
const entryId = getters['getId'](payload.entry)
|
||||
if (entryId === null) return
|
||||
|
||||
await commit('destroyLightgroup', { entryId, lightgroupId: payload.lightgroupId })
|
||||
|
||||
await dispatch('upload', entryId)
|
||||
},
|
||||
|
||||
async storePreset({ commit, dispatch, getters }, payload: payloadStorePreset) {
|
||||
let entryId = getters['getId'](payload.entry)
|
||||
if (entryId === null) entryId = await dispatch('store', payload.entry)
|
||||
|
||||
const presetId = uuidv4()
|
||||
await commit('updatePreset', { entryId, presetId, values: payload.preset })
|
||||
|
||||
await dispatch('upload', entryId)
|
||||
|
||||
return presetId
|
||||
},
|
||||
|
||||
async updatePreset({ commit, dispatch, getters }, payload: payloadStorePreset) {
|
||||
const entryId = getters['getId'](payload.entry)
|
||||
if (entryId === null) return
|
||||
|
||||
await commit('updatePreset', { entryId, presetId: payload.preset.id, values: payload.preset })
|
||||
|
||||
await dispatch('upload', entryId)
|
||||
|
||||
return payload.preset.id
|
||||
},
|
||||
|
||||
async deletePreset({ commit, dispatch, getters }, payload: payloadDeletePreset) {
|
||||
const entryId = getters['getId'](payload.entry)
|
||||
if (entryId === null) return
|
||||
|
||||
await commit('destroyPreset', { entryId, presetId: payload.presetId })
|
||||
|
||||
await dispatch('upload', entryId)
|
||||
},
|
||||
}
|
||||
|
||||
interface payloadStore {
|
||||
type: string
|
||||
name: string
|
||||
}
|
||||
|
||||
interface payloadStoreLightgroup {
|
||||
entry: {
|
||||
type: string
|
||||
name: string
|
||||
}
|
||||
lightgroup: GuiMiscellaneousStateEntryLightgroup
|
||||
}
|
||||
|
||||
interface payloadDeleteLightgroup {
|
||||
entry: {
|
||||
type: string
|
||||
name: string
|
||||
}
|
||||
lightgroupId: string
|
||||
}
|
||||
|
||||
interface payloadStorePreset {
|
||||
entry: {
|
||||
type: string
|
||||
name: string
|
||||
}
|
||||
preset: GuiMiscellaneousStateEntryPreset
|
||||
}
|
||||
|
||||
interface payloadDeletePreset {
|
||||
entry: {
|
||||
type: string
|
||||
name: string
|
||||
}
|
||||
presetId: string
|
||||
}
|
75
src/store/gui/miscellaneous/getters.ts
Normal file
75
src/store/gui/miscellaneous/getters.ts
Normal file
@ -0,0 +1,75 @@
|
||||
import { GetterTree } from 'vuex'
|
||||
import {
|
||||
GuiMiscellaneousState,
|
||||
GuiMiscellaneousStateEntry,
|
||||
GuiMiscellaneousStateEntryLightgroup,
|
||||
GuiMiscellaneousStateEntryPreset,
|
||||
} from '@/store/gui/miscellaneous/types'
|
||||
import { caseInsensitiveSort } from '@/plugins/helpers'
|
||||
|
||||
// eslint-disable-next-line
|
||||
export const getters: GetterTree<GuiMiscellaneousState, any> = {
|
||||
getEntries: (state) => {
|
||||
const output: GuiMiscellaneousStateEntry[] = []
|
||||
|
||||
Object.entries(state.entries).forEach(([key, values]) => {
|
||||
output.push({
|
||||
id: key,
|
||||
name: values.name,
|
||||
type: values.type,
|
||||
lightgroups: { ...values.lightgroups },
|
||||
presets: { ...values.presets },
|
||||
})
|
||||
})
|
||||
|
||||
return output
|
||||
},
|
||||
|
||||
getEntry: (state, getters) => (payload: { type: string; name: string }) => {
|
||||
return getters.getEntries.find(
|
||||
(entry: GuiMiscellaneousStateEntry) => entry.name === payload.name && entry.type === payload.type
|
||||
) as GuiMiscellaneousStateEntry
|
||||
},
|
||||
|
||||
getId: (state, getters) => (payload: { type: string; name: string }) => {
|
||||
return getters.getEntry(payload)?.id ?? null
|
||||
},
|
||||
|
||||
getEntryLightgroups: (state, getters) => (payload: { type: string; name: string }) => {
|
||||
const entry = getters.getEntry(payload) as GuiMiscellaneousStateEntry | null
|
||||
|
||||
if (!entry) return []
|
||||
|
||||
const groups: GuiMiscellaneousStateEntryLightgroup[] = []
|
||||
Object.entries(entry.lightgroups).forEach(([key, lightgroup]) => {
|
||||
groups.push({
|
||||
name: lightgroup.name,
|
||||
start: lightgroup.start,
|
||||
end: lightgroup.end,
|
||||
id: key,
|
||||
})
|
||||
})
|
||||
|
||||
return caseInsensitiveSort(groups, 'name')
|
||||
},
|
||||
|
||||
getEntryPresets: (state, getters) => (payload: { type: string; name: string }) => {
|
||||
const entry = getters.getEntry(payload) as GuiMiscellaneousStateEntry | null
|
||||
|
||||
if (!entry) return []
|
||||
|
||||
const presets: GuiMiscellaneousStateEntryPreset[] = []
|
||||
Object.entries(entry.presets).forEach(([key, preset]) => {
|
||||
presets.push({
|
||||
name: preset.name,
|
||||
red: preset.red,
|
||||
green: preset.green,
|
||||
blue: preset.blue,
|
||||
white: preset.white,
|
||||
id: key,
|
||||
})
|
||||
})
|
||||
|
||||
return caseInsensitiveSort(presets, 'name')
|
||||
},
|
||||
}
|
23
src/store/gui/miscellaneous/index.ts
Normal file
23
src/store/gui/miscellaneous/index.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { Module } from 'vuex'
|
||||
import { actions } from '@/store/gui/miscellaneous/actions'
|
||||
import { mutations } from '@/store/gui/miscellaneous/mutations'
|
||||
import { getters } from '@/store/gui/miscellaneous/getters'
|
||||
import { GuiMiscellaneousState } from '@/store/gui/miscellaneous/types'
|
||||
|
||||
export const getDefaultState = (): GuiMiscellaneousState => {
|
||||
return {
|
||||
entries: {},
|
||||
}
|
||||
}
|
||||
|
||||
// initial state
|
||||
const state = getDefaultState()
|
||||
|
||||
// eslint-disable-next-line
|
||||
export const miscellaneous: Module<GuiMiscellaneousState, any> = {
|
||||
namespaced: true,
|
||||
state,
|
||||
getters,
|
||||
actions,
|
||||
mutations,
|
||||
}
|
92
src/store/gui/miscellaneous/mutations.ts
Normal file
92
src/store/gui/miscellaneous/mutations.ts
Normal file
@ -0,0 +1,92 @@
|
||||
import { getDefaultState } from './index'
|
||||
import { MutationTree } from 'vuex'
|
||||
import Vue from 'vue'
|
||||
import {
|
||||
GuiMiscellaneousState,
|
||||
GuiMiscellaneousStateEntry,
|
||||
GuiMiscellaneousStateEntryLightgroup,
|
||||
GuiMiscellaneousStateEntryPreset,
|
||||
} from '@/store/gui/miscellaneous/types'
|
||||
|
||||
export const mutations: MutationTree<GuiMiscellaneousState> = {
|
||||
reset(state) {
|
||||
Object.assign(state, getDefaultState())
|
||||
},
|
||||
|
||||
store(state, payload: payloadStore) {
|
||||
const values: GuiMiscellaneousStateEntry = {
|
||||
name: payload.values.name,
|
||||
type: payload.values.type,
|
||||
lightgroups: {},
|
||||
presets: {},
|
||||
}
|
||||
|
||||
Vue.set(state.entries, payload.id, values)
|
||||
},
|
||||
|
||||
updateLightgroup(state, payload: payloadUpdateLightgroup) {
|
||||
const lightgroup: GuiMiscellaneousStateEntryLightgroup = {
|
||||
name: payload.values.name,
|
||||
start: parseInt(payload.values.start.toString()),
|
||||
end: parseInt(payload.values.end.toString()),
|
||||
}
|
||||
|
||||
Vue.set(state.entries[payload.entryId].lightgroups, payload.lightgroupId, lightgroup)
|
||||
},
|
||||
|
||||
destroyLightgroup(state, payload: payloadDestroyLightgroup) {
|
||||
const entries = { ...state.entries }
|
||||
delete entries[payload.entryId].lightgroups[payload.lightgroupId]
|
||||
|
||||
Vue.set(state, 'entries', entries)
|
||||
},
|
||||
|
||||
updatePreset(state, payload: payloadUpdatePreset) {
|
||||
const preset: GuiMiscellaneousStateEntryPreset = {
|
||||
name: payload.values.name,
|
||||
red: payload.values.red,
|
||||
green: payload.values.green,
|
||||
blue: payload.values.blue,
|
||||
white: payload.values.white,
|
||||
}
|
||||
|
||||
Vue.set(state.entries[payload.entryId].presets, payload.presetId, preset)
|
||||
},
|
||||
|
||||
destroyPreset(state, payload: payloadDestroyPreset) {
|
||||
const entries = { ...state.entries }
|
||||
delete entries[payload.entryId].presets[payload.presetId]
|
||||
|
||||
Vue.set(state, 'entries', entries)
|
||||
},
|
||||
}
|
||||
|
||||
interface payloadStore {
|
||||
id: string
|
||||
values: {
|
||||
type: string
|
||||
name: string
|
||||
}
|
||||
}
|
||||
|
||||
interface payloadUpdateLightgroup {
|
||||
entryId: string
|
||||
lightgroupId: string
|
||||
values: GuiMiscellaneousStateEntryLightgroup
|
||||
}
|
||||
|
||||
interface payloadDestroyLightgroup {
|
||||
entryId: string
|
||||
lightgroupId: string
|
||||
}
|
||||
|
||||
interface payloadUpdatePreset {
|
||||
entryId: string
|
||||
presetId: string
|
||||
values: GuiMiscellaneousStateEntryPreset
|
||||
}
|
||||
|
||||
interface payloadDestroyPreset {
|
||||
entryId: string
|
||||
presetId: string
|
||||
}
|
33
src/store/gui/miscellaneous/types.ts
Normal file
33
src/store/gui/miscellaneous/types.ts
Normal file
@ -0,0 +1,33 @@
|
||||
export interface GuiMiscellaneousState {
|
||||
entries: {
|
||||
[key: string]: GuiMiscellaneousStateEntry
|
||||
}
|
||||
}
|
||||
|
||||
export interface GuiMiscellaneousStateEntry {
|
||||
id?: string
|
||||
type: string
|
||||
name: string
|
||||
lightgroups: {
|
||||
[key: string]: GuiMiscellaneousStateEntryLightgroup
|
||||
}
|
||||
presets: {
|
||||
[key: string]: GuiMiscellaneousStateEntryPreset
|
||||
}
|
||||
}
|
||||
|
||||
export interface GuiMiscellaneousStateEntryLightgroup {
|
||||
id?: string
|
||||
name: string
|
||||
start: number
|
||||
end: number
|
||||
}
|
||||
|
||||
export interface GuiMiscellaneousStateEntryPreset {
|
||||
id?: string
|
||||
name: string
|
||||
red: number | null
|
||||
blue: number | null
|
||||
green: number | null
|
||||
white: number | null
|
||||
}
|
@ -21,6 +21,8 @@ import {
|
||||
PrinterStateTemperatureSensor,
|
||||
PrinterStateToolchangeMacro,
|
||||
PrinterStateAdditionalSensor,
|
||||
PrinterGetterObject,
|
||||
PrinterStateLight,
|
||||
} from '@/store/printer/types'
|
||||
import { caseInsensitiveSort, formatFrequency, getMacroParams } from '@/plugins/helpers'
|
||||
import { RootState } from '@/store/types'
|
||||
@ -139,6 +141,29 @@ export const getters: GetterTree<PrinterState, RootState> = {
|
||||
return 0
|
||||
},
|
||||
|
||||
getPrinterObjects: (state) => (supportedObjects: string[]) => {
|
||||
const outputObjects: PrinterGetterObject[] = []
|
||||
|
||||
for (const [key, value] of Object.entries(state)) {
|
||||
let type = key.substring(0, key.indexOf(' ')).trimEnd()
|
||||
let name = key.substring(key.indexOf(' ') + 1).trimStart()
|
||||
|
||||
if (key.indexOf(' ') === -1) type = name = key
|
||||
|
||||
if (supportedObjects.includes(type)) {
|
||||
outputObjects.push({
|
||||
name,
|
||||
type,
|
||||
state: { ...value },
|
||||
config: state.configfile?.config[key] ?? {},
|
||||
settings: state.configfile?.settings[key] ?? {},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return outputObjects
|
||||
},
|
||||
|
||||
getMacros: (state) => {
|
||||
const array: PrinterStateMacro[] = []
|
||||
const config = state.configfile?.config ?? {}
|
||||
@ -429,26 +454,21 @@ export const getters: GetterTree<PrinterState, RootState> = {
|
||||
return 'fan' in state ? state.fan.speed : 0
|
||||
},
|
||||
|
||||
getFans: (state) => {
|
||||
getFans: (state, getters) => {
|
||||
const fans: PrinterStateFan[] = []
|
||||
const supportedFans = ['temperature_fan', 'controller_fan', 'heater_fan', 'fan_generic', 'fan']
|
||||
const objects = getters.getPrinterObjects(supportedFans)
|
||||
|
||||
const controllableFans = ['fan_generic', 'fan']
|
||||
|
||||
for (const [key, value] of Object.entries(state)) {
|
||||
const nameSplit = key.split(' ')
|
||||
|
||||
if (supportedFans.includes(nameSplit[0])) {
|
||||
const name = nameSplit.length > 1 ? nameSplit[1] : nameSplit[0]
|
||||
|
||||
fans.push({
|
||||
name: name,
|
||||
type: nameSplit[0],
|
||||
speed: 'speed' in value ? value.speed : 0,
|
||||
controllable: controllableFans.includes(nameSplit[0]),
|
||||
})
|
||||
}
|
||||
}
|
||||
objects.foreach((object: PrinterGetterObject) => {
|
||||
fans.push({
|
||||
name: object.name,
|
||||
type: object.type,
|
||||
speed: object.state.speed ?? 0,
|
||||
controllable: controllableFans.includes(object.type),
|
||||
})
|
||||
})
|
||||
|
||||
return fans.sort((a, b) => {
|
||||
if (a.controllable < b.controllable) return 1
|
||||
@ -464,6 +484,86 @@ export const getters: GetterTree<PrinterState, RootState> = {
|
||||
})
|
||||
},
|
||||
|
||||
getLights: (state, getters) => {
|
||||
const lights: PrinterStateLight[] = []
|
||||
const supportedObjects = ['dotstar', 'led', 'neopixel', 'pca9533', 'pca9632']
|
||||
const objects = getters.getPrinterObjects(supportedObjects)
|
||||
|
||||
objects
|
||||
.filter((object: PrinterGetterObject) => {
|
||||
return !object.name.startsWith('_')
|
||||
})
|
||||
.forEach((object: PrinterGetterObject) => {
|
||||
let colorOrder = 'RGB'
|
||||
let singleChannelTarget = null
|
||||
const colorData = object.state.color_data ?? []
|
||||
|
||||
if ('color_order' in object.settings) colorOrder = object.settings.color_order[0] ?? ''
|
||||
|
||||
if (object.type === 'led') {
|
||||
colorOrder = ''
|
||||
if ('red_pin' in object.config) colorOrder += 'R'
|
||||
if ('green_pin' in object.config) colorOrder += 'G'
|
||||
if ('blue_pin' in object.config) colorOrder += 'B'
|
||||
if ('white_pin' in object.config) colorOrder += 'W'
|
||||
}
|
||||
|
||||
let initialRed = object.settings.initial_red ?? null
|
||||
if (!('initial_red' in object.config)) initialRed = null
|
||||
|
||||
let initialGreen = object.settings.initial_green ?? null
|
||||
if (!('initial_green' in object.config)) initialGreen = null
|
||||
|
||||
let initialBlue = object.settings.initial_blue ?? null
|
||||
if (!('initial_blue' in object.config)) initialBlue = null
|
||||
|
||||
let initialWhite = object.settings.initial_white ?? null
|
||||
if (!('initial_white' in object.config)) initialWhite = null
|
||||
|
||||
if (object.type === 'led' && colorOrder.length === 1) {
|
||||
const firstColorData = colorData[0] ?? []
|
||||
|
||||
switch (colorOrder) {
|
||||
case 'R':
|
||||
singleChannelTarget = firstColorData[0] ?? 0
|
||||
break
|
||||
case 'G':
|
||||
singleChannelTarget = firstColorData[1] ?? 0
|
||||
break
|
||||
case 'B':
|
||||
singleChannelTarget = firstColorData[2] ?? 0
|
||||
break
|
||||
case 'W':
|
||||
singleChannelTarget = firstColorData[3] ?? 0
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
lights.push({
|
||||
name: object.name,
|
||||
type: object.type as PrinterStateLight['type'],
|
||||
chainCount: object.settings.chain_count ?? 1,
|
||||
colorOrder,
|
||||
initialRed,
|
||||
initialGreen,
|
||||
initialBlue,
|
||||
initialWhite,
|
||||
colorData,
|
||||
singleChannelTarget,
|
||||
})
|
||||
})
|
||||
|
||||
return lights.sort((a, b) => {
|
||||
const nameA = a.name.toUpperCase()
|
||||
const nameB = b.name.toUpperCase()
|
||||
|
||||
if (nameA < nameB) return -1
|
||||
if (nameA > nameB) return 1
|
||||
|
||||
return 0
|
||||
})
|
||||
},
|
||||
|
||||
getMiscellaneous: (state) => {
|
||||
const output: PrinterStateMiscellaneous[] = []
|
||||
const supportedObjects = ['controller_fan', 'heater_fan', 'fan_generic', 'fan', 'output_pin']
|
||||
|
@ -108,6 +108,32 @@ export interface PrinterStateFan {
|
||||
controllable: boolean
|
||||
}
|
||||
|
||||
export interface PrinterStateLight {
|
||||
name: string
|
||||
type: 'led' | 'neopixel' | 'dotstar' | 'pca9533' | 'pca9632'
|
||||
colorOrder: string
|
||||
chainCount: number
|
||||
initialRed: number | null
|
||||
initialGreen: number | null
|
||||
initialBlue: number | null
|
||||
initialWhite: number | null
|
||||
colorData: number[][]
|
||||
singleChannelTarget: number | null
|
||||
}
|
||||
|
||||
export interface PrinterStateLight {
|
||||
name: string
|
||||
type: 'led' | 'neopixel' | 'dotstar' | 'pca9533' | 'pca9632'
|
||||
colorOrder: string
|
||||
chainCount: number
|
||||
initialRed: number | null
|
||||
initialGreen: number | null
|
||||
initialBlue: number | null
|
||||
initialWhite: number | null
|
||||
colorData: number[][]
|
||||
singleChannelTarget: number | null
|
||||
}
|
||||
|
||||
export interface PrinterStateMiscellaneous {
|
||||
name: string
|
||||
type: string
|
||||
@ -227,3 +253,17 @@ export interface PrinterStateToolchangeMacro {
|
||||
name: string
|
||||
active: boolean
|
||||
}
|
||||
|
||||
export interface PrinterGetterObject {
|
||||
name: string
|
||||
type: string
|
||||
state: {
|
||||
[key: string]: any
|
||||
}
|
||||
config: {
|
||||
[key: string]: string
|
||||
}
|
||||
settings: {
|
||||
[key: string]: any
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user