Skip to content

Commit be67ba0

Browse files
committed
feat: moonraker power plugin support
1 parent 18e89a2 commit be67ba0

15 files changed

Lines changed: 222 additions & 15 deletions

File tree

src/components/cards/ToolsCard.vue

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,41 +6,48 @@
66
fixed-tabs
77
background-color="quaternary"
88
>
9-
<v-tab :key="0">
9+
<v-tab :key="'macros'">
1010
<v-icon left>{{ icons.fileCode }}</v-icon>
1111
Macros
1212
</v-tab>
13-
<v-tab :key="1">
13+
<v-tab :key="'power'" v-if="gpioPowerPluginEnabled">
14+
<v-icon left>{{ icons.power }}</v-icon>
15+
Power
16+
</v-tab>
17+
<v-tab :key="'syscommands'">
1418
<v-icon left>{{ icons.tools }}</v-icon>
1519
Sys Commands
1620
</v-tab>
17-
<v-tab :key="2">
21+
<v-tab :key="'jobs'">
1822
<v-icon left>{{ icons.files }}</v-icon>
1923
Jobs
2024
</v-tab>
21-
<v-tab :key="3">
25+
<v-tab :key="'console'">
2226
<v-icon left>{{ icons.console }}</v-icon>
2327
Console
2428
</v-tab>
2529
</v-tabs>
2630
<v-divider></v-divider>
2731

2832
<v-tabs-items v-model="activeTab" class="mb-auto rounded">
29-
<v-tab-item :key="0" class="tertiary rounded">
33+
<v-tab-item :key="'macros'" class="tertiary rounded">
3034
<macros-widget></macros-widget>
3135
</v-tab-item>
32-
<v-tab-item :key="1" class="tertiary rounded">
36+
<v-tab-item :key="'power'" class="tertiary rounded" v-if="gpioPowerPluginEnabled">
37+
<power-control-widget></power-control-widget>
38+
</v-tab-item>
39+
<v-tab-item :key="'syscommands'" class="tertiary rounded">
3340
<system-commands-widget></system-commands-widget>
3441
</v-tab-item>
35-
<v-tab-item :key="2" class="tertiary rounded max-height">
42+
<v-tab-item :key="'jobs'" class="tertiary rounded max-height">
3643
<file-system-widget
3744
root="gcodes"
3845
accept=".gcode"
3946
:show-title="false"
4047
:show-meta-data="false"
4148
></file-system-widget>
4249
</v-tab-item>
43-
<v-tab-item :key="3" class="tertiary rounded max-height">
50+
<v-tab-item :key="'console'" class="tertiary rounded max-height">
4451
<console-widget></console-widget>
4552
</v-tab-item>
4653
</v-tabs-items>
@@ -55,17 +62,23 @@ import MacrosWidget from '@/components/widgets/MacrosWidget.vue'
5562
import FileSystemWidget from '@/components/widgets/filesystem/FileSystemWidget.vue'
5663
import SystemCommandsWidget from '@/components/widgets/SystemCommandsWidget.vue'
5764
import ConsoleWidget from '@/components/widgets/ConsoleWidget.vue'
65+
import PowerControlWidget from '@/components/widgets/PowerControlWidget.vue'
5866
5967
@Component({
6068
components: {
6169
MacrosWidget,
6270
FileSystemWidget,
6371
SystemCommandsWidget,
64-
ConsoleWidget
72+
ConsoleWidget,
73+
PowerControlWidget
6574
}
6675
})
6776
export default class ToolsCard extends Mixins(UtilsMixin) {
68-
activeTab = 0
77+
activeTab = 'macros'
78+
79+
get gpioPowerPluginEnabled () {
80+
return (this.$store.state.socket.plugins.includes('power'))
81+
}
6982
}
7083
</script>
7184

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<template>
2+
<v-card-text class="d-flex flex-wrap align-center justify-start pt-5">
3+
<v-btn
4+
v-for="(device, index) in devices"
5+
:key="index"
6+
@click="toggleDevice(device, `${waits.onGpio}${device.id}`)"
7+
:loading="hasWait(`${waits.onGpio}${device.id}`)"
8+
color="secondary"
9+
class="me-2 mb-2">Toggle {{ device.name }} {{ (device.state === 1) ? 'Off' : 'On' }}</v-btn>
10+
</v-card-text>
11+
</template>
12+
13+
<script lang="ts">
14+
import { Component, Mixins } from 'vue-property-decorator'
15+
import UtilsMixin from '@/mixins/utils'
16+
import { Waits } from '@/globals'
17+
import { SocketActions } from '@/socketActions'
18+
import { Device } from '@/store/gpio/types'
19+
20+
@Component({})
21+
export default class PowerControlWidget extends Mixins(UtilsMixin) {
22+
waits = Waits
23+
24+
get devices () {
25+
return this.$store.state.gpio.devices
26+
}
27+
28+
toggleDevice (device: Device, wait?: string) {
29+
const state = (device.state === 1) ? 0 : 1
30+
if (wait) this.$store.dispatch('socket/addWait', wait)
31+
SocketActions.machineGpioPowerToggle(device.id, state, wait)
32+
}
33+
}
34+
</script>

src/globals.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ import {
5353
mdiDeleteAlertOutline,
5454
mdiCogs,
5555
mdiContentSaveOutline,
56-
mdiAlert
56+
mdiAlert,
57+
mdiPowerPlug
5758
} from '@mdi/js'
5859

5960
/**
@@ -70,6 +71,7 @@ export const Globals = Object.freeze({
7071
})
7172

7273
export const Icons = Object.freeze({
74+
power: mdiPowerPlug,
7375
home: mdiHome,
7476
close: mdiClose,
7577
refresh: mdiRefresh,
@@ -128,6 +130,7 @@ export const Icons = Object.freeze({
128130
})
129131

130132
export const Waits = Object.freeze({
133+
onGpio: 'onGpio',
131134
onHomeAll: 'onHomeAll',
132135
onHomeXY: 'onHomeXY',
133136
onHomeZ: 'onHomeZ',

src/socketActions.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import Vue from 'vue'
22
import { Waits } from '@/globals'
3+
import { Device } from './store/gpio/types'
34

45
export const SocketActions = {
56
async printerInfo () {
@@ -30,6 +31,35 @@ export const SocketActions = {
3031
)
3132
},
3233

34+
async machineGpioPowerDevices () {
35+
Vue.$socket.emit(
36+
'machine.gpio_power.devices', {
37+
action: 'gpio/init'
38+
}
39+
)
40+
},
41+
42+
async machineGpioPowerStatus () {
43+
Vue.$socket.emit(
44+
'machine.gpio_power.status', {
45+
action: 'gpio/onStatus'
46+
}
47+
)
48+
},
49+
50+
async machineGpioPowerToggle (id: string, state: number, wait?: string) {
51+
const emit = (state === 1)
52+
? 'machine.gpio_power.on'
53+
: 'machine.gpio_power.off'
54+
Vue.$socket.emit(
55+
emit, {
56+
action: 'gpio/onToggle',
57+
params: { [id]: null },
58+
wait
59+
}
60+
)
61+
},
62+
3363
async printerQueryEndstops () {
3464
Vue.$socket.emit(
3565
'printer.query_endstops.status', {

src/store/gpio/actions.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { ActionTree } from 'vuex'
2+
import { GpioState } from './types'
3+
import { RootState } from '../types'
4+
import { SocketActions } from '@/socketActions'
5+
6+
export const actions: ActionTree<GpioState, RootState> = {
7+
8+
/**
9+
* Inits the list of available devices.
10+
*/
11+
async init ({ commit }, payload) {
12+
if (
13+
payload.devices &&
14+
payload.devices.length > 0
15+
) {
16+
commit('onDevices', payload)
17+
SocketActions.machineGpioPowerStatus()
18+
}
19+
},
20+
21+
/**
22+
* Loads the current status of each power device defined.
23+
*/
24+
async onStatus ({ commit }, payload) {
25+
commit('onStatus', payload)
26+
},
27+
28+
/**
29+
* On a toggling a power device.
30+
*/
31+
async onToggle ({ commit, dispatch }, payload) {
32+
dispatch('onStatus', payload)
33+
34+
// Remove a wait if defined.
35+
if (payload.__request__ && payload.__request__.wait && payload.__request__.wait.length) {
36+
commit('socket/removeWait', payload.__request__.wait, { root: true })
37+
}
38+
}
39+
40+
}

src/store/gpio/getters.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { GetterTree } from 'vuex'
2+
import { GpioState } from './types'
3+
import { RootState } from '../types'
4+
5+
export const getters: GetterTree<GpioState, RootState> = {
6+
}

src/store/gpio/index.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/* eslint-disable @typescript-eslint/camelcase */
2+
3+
import { Module } from 'vuex'
4+
import { getters } from './getters'
5+
import { actions } from './actions'
6+
import { mutations } from './mutations'
7+
import { GpioState } from './types'
8+
import { RootState } from '../types'
9+
10+
export const state: GpioState = {
11+
devices: []
12+
}
13+
14+
const namespaced = true
15+
16+
export const gpio: Module<GpioState, RootState> = {
17+
namespaced,
18+
state,
19+
getters,
20+
actions,
21+
mutations
22+
}

src/store/gpio/mutations.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import Vue from 'vue'
2+
import { MutationTree } from 'vuex'
3+
import { GpioState } from './types'
4+
5+
export const mutations: MutationTree<GpioState> = {
6+
onDevices (state, payload) {
7+
state.devices = payload.devices
8+
},
9+
10+
onStatus (state, payload) {
11+
for (const key in payload) {
12+
const i = state.devices.findIndex(device => device.id === key)
13+
if (i >= 0) {
14+
// Vue.set(state.devices, i, payload[key] === 'off' ? 0 : 1)
15+
Vue.set(state.devices[i], 'state', payload[key] === 'off' ? 0 : 1)
16+
}
17+
}
18+
}
19+
}

src/store/gpio/types.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export interface GpioState {
2+
devices: Device[];
3+
}
4+
5+
export interface Device {
6+
id: string;
7+
name: string;
8+
state?: 1 | 0;
9+
}

src/store/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import Vuex from 'vuex'
33
import { socket } from './socket'
44
import { files } from './files'
55
import { config } from './config'
6+
import { gpio } from './gpio'
67
import { RootState } from './types'
78
import { FileConfig } from './config/types'
89

@@ -17,7 +18,8 @@ export default new Vuex.Store<RootState>({
1718
modules: {
1819
config,
1920
socket,
20-
files
21+
files,
22+
gpio
2123
},
2224
mutations: {
2325
setVersion (state, payload) {

0 commit comments

Comments
 (0)