MQTT Button works
This commit is contained in:
parent
5e9e0cccc3
commit
6579a3ba52
|
|
@ -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'
|
||||||
|
)
|
||||||
|
|
@ -84,6 +84,10 @@
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
|
||||||
|
&.hidden-title {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
.drag-handle {
|
.drag-handle {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
cursor: move;
|
cursor: move;
|
||||||
|
|
@ -133,6 +137,21 @@
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mqtt-button {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
&.apply-stretch {
|
||||||
|
align-items: stretch;
|
||||||
|
|
||||||
|
> button {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { useConfirmModal } from '@/contexts/ConfirmModalsContext'
|
||||||
import {
|
import {
|
||||||
DashboardDialData,
|
DashboardDialData,
|
||||||
DashboardGraphData,
|
DashboardGraphData,
|
||||||
|
DashboardMQTTButtonData,
|
||||||
} from '@/utils/dashboard/parseDashboard'
|
} from '@/utils/dashboard/parseDashboard'
|
||||||
import { useForm } from '@/utils/hooks/useForm'
|
import { useForm } from '@/utils/hooks/useForm'
|
||||||
import { useState } from 'preact/hooks'
|
import { useState } from 'preact/hooks'
|
||||||
|
|
@ -11,6 +12,7 @@ import { useQuery } from 'react-query'
|
||||||
import { BoxDefinition } from '../../types'
|
import { BoxDefinition } from '../../types'
|
||||||
import { DialSettings } from './components/DialSettings'
|
import { DialSettings } from './components/DialSettings'
|
||||||
import { GraphSettings } from './components/GraphSettings'
|
import { GraphSettings } from './components/GraphSettings'
|
||||||
|
import { MQTTButtonSettings } from './components/MQTTButtonSettings'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
value: BoxDefinition
|
value: BoxDefinition
|
||||||
|
|
@ -63,6 +65,7 @@ export const BoxSettings = ({ value, onSave, onClose, onRemove }: Props) => {
|
||||||
<select {...register('type')} required>
|
<select {...register('type')} required>
|
||||||
<option value="graph">Graph</option>
|
<option value="graph">Graph</option>
|
||||||
<option value="dial">Dial</option>
|
<option value="dial">Dial</option>
|
||||||
|
<option value="mqttButton">MQTT Button</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -81,6 +84,13 @@ export const BoxSettings = ({ value, onSave, onClose, onRemove }: Props) => {
|
||||||
sensors={sensors.data ?? []}
|
sensors={sensors.data ?? []}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{type === 'mqttButton' && (
|
||||||
|
<MQTTButtonSettings
|
||||||
|
value={data as DashboardMQTTButtonData}
|
||||||
|
onChange={setData}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 (
|
||||||
|
<>
|
||||||
|
<FormField
|
||||||
|
name="server"
|
||||||
|
label="MQTT Broker Address"
|
||||||
|
hint="Example: tcp://10.10.1.1:1883"
|
||||||
|
>
|
||||||
|
<input required {...register('server')} />
|
||||||
|
</FormField>
|
||||||
|
<FormField name="clientId" label="Client ID">
|
||||||
|
<input {...register('clientId')} />
|
||||||
|
</FormField>
|
||||||
|
<FormField name="username" label="Username">
|
||||||
|
<input {...register('username')} />
|
||||||
|
</FormField>
|
||||||
|
<FormField name="password" label="Password">
|
||||||
|
<input type="password" {...register('password')} />
|
||||||
|
</FormField>
|
||||||
|
<FormField name="qoc" label="QoS">
|
||||||
|
<select {...register('qos', { type: 'integer' })}>
|
||||||
|
<option value="0">0</option>
|
||||||
|
<option value="1">1</option>
|
||||||
|
<option value="2">2</option>
|
||||||
|
</select>
|
||||||
|
</FormField>
|
||||||
|
<FormCheckboxField name="retain" label="Retain">
|
||||||
|
<input type="checkbox" {...register('retain')} />
|
||||||
|
</FormCheckboxField>
|
||||||
|
<FormField name="topic" label="Topic">
|
||||||
|
<input required {...register('topic')} />
|
||||||
|
</FormField>
|
||||||
|
<FormField name="message" label="Payload">
|
||||||
|
<textarea required {...register('message')} rows={4} />
|
||||||
|
</FormField>
|
||||||
|
<FormCheckboxField
|
||||||
|
name="stretchButton"
|
||||||
|
label="Stretch button to full size"
|
||||||
|
hint="Makes button cover the whole area of the box"
|
||||||
|
>
|
||||||
|
<input type="checkbox" {...register('stretchButton')} />
|
||||||
|
</FormCheckboxField>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -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 (
|
||||||
|
<EditableBox {...boxProps} onRefresh={() => null} hiddenTitle>
|
||||||
|
<div className={cn('mqtt-button', data.stretchButton && 'apply-stretch')}>
|
||||||
|
<button onClick={onClick} disabled={pushMutation.isLoading}>
|
||||||
|
{boxProps.box.title ?? 'BUTTON'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</EditableBox>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -9,6 +9,7 @@ import { useWindowEvent } from '@/utils/hooks/useWindowEvent'
|
||||||
import { ComponentChild } from 'preact'
|
import { ComponentChild } from 'preact'
|
||||||
import { useState } from 'preact/hooks'
|
import { useState } from 'preact/hooks'
|
||||||
import { BoxSettings } from '../../BoxSettings/BoxSettings'
|
import { BoxSettings } from '../../BoxSettings/BoxSettings'
|
||||||
|
import { cn } from '@/utils/cn'
|
||||||
|
|
||||||
export type EditableBoxProps = {
|
export type EditableBoxProps = {
|
||||||
box: BoxDefinition
|
box: BoxDefinition
|
||||||
|
|
@ -19,11 +20,13 @@ export type EditableBoxProps = {
|
||||||
}
|
}
|
||||||
|
|
||||||
type EditableBoxPropsWithExtra = EditableBoxProps & {
|
type EditableBoxPropsWithExtra = EditableBoxProps & {
|
||||||
|
hiddenTitle?: boolean
|
||||||
children: ComponentChild
|
children: ComponentChild
|
||||||
onRefresh: () => void
|
onRefresh: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export const EditableBox = ({
|
export const EditableBox = ({
|
||||||
|
hiddenTitle,
|
||||||
box,
|
box,
|
||||||
children,
|
children,
|
||||||
onPosition,
|
onPosition,
|
||||||
|
|
@ -135,9 +138,9 @@ export const EditableBox = ({
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div className="box" style={{ height: '100%' }}>
|
<div className="box" style={{ height: '100%' }}>
|
||||||
<div className="header">
|
<div className={cn('header', hiddenTitle && 'hidden-title')}>
|
||||||
<div className="drag-handle" onMouseDown={handleMouseDown}>
|
<div className="drag-handle" onMouseDown={handleMouseDown}>
|
||||||
<div className="name">{box.title || ''}</div>
|
{!hiddenTitle && <div className="name">{box.title || ''}</div>}
|
||||||
</div>
|
</div>
|
||||||
<div className="actions">
|
<div className="actions">
|
||||||
<div className="action" onClick={onRefresh}>
|
<div className="action" onClick={onRefresh}>
|
||||||
|
|
@ -148,6 +151,7 @@ export const EditableBox = ({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="body">{children}</div>
|
<div className="body">{children}</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { BoxDialContent } from './BoxDialContent'
|
import { BoxDialContent } from './BoxDialContent'
|
||||||
import { BoxGraphContent } from './BoxGraphContent'
|
import { BoxGraphContent } from './BoxGraphContent'
|
||||||
|
import { BoxMQTTButtonContent } from './BoxMQTTButtonContent'
|
||||||
import { EditableBox, EditableBoxProps } from './EditableBox'
|
import { EditableBox, EditableBoxProps } from './EditableBox'
|
||||||
|
|
||||||
export const GeneralBox = (props: EditableBoxProps) => {
|
export const GeneralBox = (props: EditableBoxProps) => {
|
||||||
|
|
@ -8,10 +9,12 @@ export const GeneralBox = (props: EditableBoxProps) => {
|
||||||
return <BoxDialContent {...props} data={props.box.data} />
|
return <BoxDialContent {...props} data={props.box.data} />
|
||||||
case 'graph':
|
case 'graph':
|
||||||
return <BoxGraphContent {...props} data={props.box.data} />
|
return <BoxGraphContent {...props} data={props.box.data} />
|
||||||
|
case 'mqttButton':
|
||||||
|
return <BoxMQTTButtonContent {...props} data={props.box.data} />
|
||||||
default:
|
default:
|
||||||
return (
|
return (
|
||||||
<EditableBox {...props} onRefresh={() => null}>
|
<EditableBox {...props} onRefresh={() => null}>
|
||||||
<></>
|
<button onClick={() => props.onEdit(props.box)}>Pick a type</button>
|
||||||
</EditableBox>
|
</EditableBox>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ export type DashboardContentBox = {
|
||||||
w: number
|
w: number
|
||||||
h: number
|
h: number
|
||||||
title?: string
|
title?: string
|
||||||
data?: DashboardGraphData | DashboardDialData
|
data?: DashboardGraphData | DashboardDialData | DashboardMQTTButtonData
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DashboardGraphData = {
|
export type DashboardGraphData = {
|
||||||
|
|
@ -44,6 +44,19 @@ export type DashboardDialData = {
|
||||||
fontSize?: number
|
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) => {
|
export const parseDashboard = (input: string) => {
|
||||||
const data = JSON.parse(input) as DashboardContent
|
const data = JSON.parse(input) as DashboardContent
|
||||||
const migrations = getDashboardMigrations(data.version)
|
const migrations = getDashboardMigrations(data.version)
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import (
|
||||||
|
|
||||||
type putMQTTPublishBody struct {
|
type putMQTTPublishBody struct {
|
||||||
Server string `json:"server" binding:"required"`
|
Server string `json:"server" binding:"required"`
|
||||||
ClientId string `json:"clientId" binding:"required"`
|
ClientId string `json:"clientId"`
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
Retain *bool `json:"retain"`
|
Retain *bool `json:"retain"`
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue