diff --git a/client/src/api/mqtt.ts b/client/src/api/mqtt.ts
new file mode 100644
index 0000000..9b46b83
--- /dev/null
+++ b/client/src/api/mqtt.ts
@@ -0,0 +1,21 @@
+import { request } from './request'
+
+export const publishMqttMessage = (body: {
+ server: string
+ clientId?: string
+ username?: string
+ password?: string
+ qos?: number
+ retain?: boolean
+ message: string
+ topic: string
+}) =>
+ request(
+ `/api/mqtt/publish`,
+ {
+ method: 'POST',
+ headers: { 'content-type': 'application/json' },
+ body: JSON.stringify(body),
+ },
+ 'void'
+ )
diff --git a/client/src/assets/components/_grid-sensors.scss b/client/src/assets/components/_grid-sensors.scss
index 557d6ee..102b8d6 100644
--- a/client/src/assets/components/_grid-sensors.scss
+++ b/client/src/assets/components/_grid-sensors.scss
@@ -84,6 +84,10 @@
overflow: hidden;
text-overflow: ellipsis;
+ &.hidden-title {
+ border-bottom: none;
+ }
+
.drag-handle {
flex: 1;
cursor: move;
@@ -133,6 +137,21 @@
justify-content: center;
height: 100%;
}
+
+ .mqtt-button {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: 100%;
+
+ &.apply-stretch {
+ align-items: stretch;
+
+ > button {
+ flex: 1;
+ }
+ }
+ }
}
}
diff --git a/client/src/pages/dashboard/components/BoxSettings/BoxSettings.tsx b/client/src/pages/dashboard/components/BoxSettings/BoxSettings.tsx
index 3af8b35..5402262 100644
--- a/client/src/pages/dashboard/components/BoxSettings/BoxSettings.tsx
+++ b/client/src/pages/dashboard/components/BoxSettings/BoxSettings.tsx
@@ -4,6 +4,7 @@ import { useConfirmModal } from '@/contexts/ConfirmModalsContext'
import {
DashboardDialData,
DashboardGraphData,
+ DashboardMQTTButtonData,
} from '@/utils/dashboard/parseDashboard'
import { useForm } from '@/utils/hooks/useForm'
import { useState } from 'preact/hooks'
@@ -11,6 +12,7 @@ import { useQuery } from 'react-query'
import { BoxDefinition } from '../../types'
import { DialSettings } from './components/DialSettings'
import { GraphSettings } from './components/GraphSettings'
+import { MQTTButtonSettings } from './components/MQTTButtonSettings'
type Props = {
value: BoxDefinition
@@ -63,6 +65,7 @@ export const BoxSettings = ({ value, onSave, onClose, onRemove }: Props) => {
@@ -81,6 +84,13 @@ export const BoxSettings = ({ value, onSave, onClose, onRemove }: Props) => {
sensors={sensors.data ?? []}
/>
)}
+
+ {type === 'mqttButton' && (
+
+ )}
>
}
diff --git a/client/src/pages/dashboard/components/BoxSettings/components/MQTTButtonSettings.tsx b/client/src/pages/dashboard/components/BoxSettings/components/MQTTButtonSettings.tsx
new file mode 100644
index 0000000..66cd703
--- /dev/null
+++ b/client/src/pages/dashboard/components/BoxSettings/components/MQTTButtonSettings.tsx
@@ -0,0 +1,67 @@
+import { FormCheckboxField } from '@/components/FormCheckboxField'
+import { FormField } from '@/components/FormField'
+import { DashboardMQTTButtonData } from '@/utils/dashboard/parseDashboard'
+import { useForm } from '@/utils/hooks/useForm'
+import { omit } from '@/utils/omit'
+
+type Props = {
+ value?: DashboardMQTTButtonData
+ onChange: (data: DashboardMQTTButtonData) => void
+}
+
+export const MQTTButtonSettings = ({ value, onChange }: Props) => {
+ const { register } = useForm({
+ defaultValue: () => ({
+ server: '',
+ topic: '',
+ message: '',
+ retain: false,
+ ...(value && omit(value, ['type'])),
+ }),
+ onChange: (v) => onChange({ ...v, type: 'mqttButton' }),
+ })
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ )
+}
diff --git a/client/src/pages/dashboard/components/DashboardGrid/components/BoxMQTTButtonContent.tsx b/client/src/pages/dashboard/components/DashboardGrid/components/BoxMQTTButtonContent.tsx
new file mode 100644
index 0000000..d49b482
--- /dev/null
+++ b/client/src/pages/dashboard/components/DashboardGrid/components/BoxMQTTButtonContent.tsx
@@ -0,0 +1,29 @@
+import { DashboardMQTTButtonData } from '@/utils/dashboard/parseDashboard'
+import { EditableBox, EditableBoxProps } from './EditableBox'
+import { useMutation } from 'react-query'
+import { publishMqttMessage } from '@/api/mqtt'
+import { cn } from '@/utils/cn'
+
+type Props = EditableBoxProps & {
+ data: DashboardMQTTButtonData
+}
+
+export const BoxMQTTButtonContent = ({ data, ...boxProps }: Props) => {
+ const pushMutation = useMutation(publishMqttMessage)
+
+ const onClick = () => {
+ pushMutation.mutate({
+ ...data,
+ })
+ }
+
+ return (
+ null} hiddenTitle>
+
+
+
+
+ )
+}
diff --git a/client/src/pages/dashboard/components/DashboardGrid/components/EditableBox.tsx b/client/src/pages/dashboard/components/DashboardGrid/components/EditableBox.tsx
index a89bd65..a614f8f 100644
--- a/client/src/pages/dashboard/components/DashboardGrid/components/EditableBox.tsx
+++ b/client/src/pages/dashboard/components/DashboardGrid/components/EditableBox.tsx
@@ -9,6 +9,7 @@ import { useWindowEvent } from '@/utils/hooks/useWindowEvent'
import { ComponentChild } from 'preact'
import { useState } from 'preact/hooks'
import { BoxSettings } from '../../BoxSettings/BoxSettings'
+import { cn } from '@/utils/cn'
export type EditableBoxProps = {
box: BoxDefinition
@@ -19,11 +20,13 @@ export type EditableBoxProps = {
}
type EditableBoxPropsWithExtra = EditableBoxProps & {
+ hiddenTitle?: boolean
children: ComponentChild
onRefresh: () => void
}
export const EditableBox = ({
+ hiddenTitle,
box,
children,
onPosition,
@@ -135,9 +138,9 @@ export const EditableBox = ({
}
>
-
+
-
{box.title || ''}
+ {!hiddenTitle &&
{box.title || ''}
}
@@ -148,6 +151,7 @@ export const EditableBox = ({
+
{children}
{
@@ -8,10 +9,12 @@ export const GeneralBox = (props: EditableBoxProps) => {
return
case 'graph':
return
+ case 'mqttButton':
+ return
default:
return (
null}>
- <>>
+
)
}
diff --git a/client/src/utils/dashboard/parseDashboard.ts b/client/src/utils/dashboard/parseDashboard.ts
index 85271dc..0d66f9b 100644
--- a/client/src/utils/dashboard/parseDashboard.ts
+++ b/client/src/utils/dashboard/parseDashboard.ts
@@ -12,7 +12,7 @@ export type DashboardContentBox = {
w: number
h: number
title?: string
- data?: DashboardGraphData | DashboardDialData
+ data?: DashboardGraphData | DashboardDialData | DashboardMQTTButtonData
}
export type DashboardGraphData = {
@@ -44,6 +44,19 @@ export type DashboardDialData = {
fontSize?: number
}
+export type DashboardMQTTButtonData = {
+ type: 'mqttButton'
+ server: string
+ clientId?: string
+ username?: string
+ password?: string
+ qos?: number
+ retain?: boolean
+ message: string
+ topic: string
+ stretchButton?: boolean
+}
+
export const parseDashboard = (input: string) => {
const data = JSON.parse(input) as DashboardContent
const migrations = getDashboardMigrations(data.version)
diff --git a/server/routes/mqtt.go b/server/routes/mqtt.go
index 5b53214..e056716 100644
--- a/server/routes/mqtt.go
+++ b/server/routes/mqtt.go
@@ -9,7 +9,7 @@ import (
type putMQTTPublishBody struct {
Server string `json:"server" binding:"required"`
- ClientId string `json:"clientId" binding:"required"`
+ ClientId string `json:"clientId"`
Username string `json:"username"`
Password string `json:"password"`
Retain *bool `json:"retain"`