Added support for dial box

This commit is contained in:
Jan Zípek 2022-08-24 12:50:00 +02:00
parent 39b7d5e73a
commit 009332dccf
Signed by: kamen
GPG Key ID: A17882625B33AC31
11 changed files with 133 additions and 11 deletions

View File

@ -19,3 +19,16 @@ export const getSensorValues = ({
from.getTime() / 1000
)}&to=${Math.round(to.getTime() / 1000)}`
)
export const getLatestSensorValue = ({
to,
sensor,
}: {
to: Date
sensor: string
}) =>
request<SensorValue>(
`/api/sensors/${encodeURI(sensor)}/values/latest?to=${Math.round(
to.getTime() / 1000
)}`
)

View File

@ -214,6 +214,8 @@ form.horizontal .input label {
.grid-sensors .grid-box .box {
position: relative;
display: flex;
flex-direction: column;
}
.grid-sensors .grid-box .box .resize-h {
@ -245,6 +247,8 @@ form.horizontal .input label {
.grid-sensors .grid-box .box .header {
display: flex;
flex-grow: 0;
flex-shrink: 0;
}
.grid-sensors .grid-box .box .header .drag-handle {
@ -256,6 +260,19 @@ form.horizontal .input label {
margin-left: auto;
}
.grid-sensors .grid-box .box .body {
flex: 1;
}
.grid-sensors .grid-box .box .dial {
text-align: center;
font-size: 150%;
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}
.grid-sensors .box-preview {
position: absolute;
background-color: #3988FF;

View File

@ -59,8 +59,7 @@ export const NewDashboardPage = () => {
return b
})
setBoxes([box, ...otherBoxes])
// TODO: Save
handleChange(() => [box, ...otherBoxes])
}
// Terrible code - ensure there's default dashboard

View File

@ -0,0 +1,34 @@
import { getLatestSensorValue } from '@/api/sensorValues'
import { DashboardDialData } from '@/utils/parseDashboard'
import { useQuery } from 'react-query'
import { useDashboardContext } from '../contexts/DashboardContext'
import { BoxDefinition } from '../types'
type Props = {
box: BoxDefinition
data: DashboardDialData
}
export const BoxDialContent = ({ box, data }: Props) => {
const { filter } = useDashboardContext()
const valuesQuery = {
sensor: box.sensor ?? '-1',
to: filter.customTo,
}
const value = useQuery(['/sensor/values/latest', valuesQuery], () =>
getLatestSensorValue(valuesQuery)
)
return (
<div className="dial">
{value.data && (
<>
{value.data.value}
{data.unit}
</>
)}
</div>
)
}

View File

@ -1,7 +1,9 @@
import { getSensors } from '@/api/sensors'
import { DashboardDialData, DashboardGraphData } from '@/utils/parseDashboard'
import { useState } from 'preact/hooks'
import { useQuery } from 'react-query'
import { BoxDefinition } from '../../types'
import { DialSettings } from './components/DialSettings'
import { GraphSettings } from './components/GraphSettings'
type Props = {
@ -88,11 +90,22 @@ export const BoxSettings = ({ value, onSave, onClose }: Props) => {
onChange={handleChange}
>
<option value="graph">Graph</option>
<option value="dial">Dial</option>
</select>
</div>
{formState.type === 'graph' && (
<GraphSettings value={data} onChange={setData} />
<GraphSettings
value={data as DashboardGraphData}
onChange={setData}
/>
)}
{formState.type === 'dial' && (
<DialSettings
value={data as DashboardDialData}
onChange={setData}
/>
)}
<div className="actions">

View File

@ -0,0 +1,33 @@
import { DashboardDialData } from '@/utils/parseDashboard'
import { useEffect, useState } from 'preact/hooks'
type Props = {
value?: DashboardDialData
onChange: (data: DashboardDialData) => void
}
export const DialSettings = ({ value, onChange }: Props) => {
const [formState, setFormState] = useState(() => ({ unit: value?.unit }))
const handleChange = (e: Event) => {
const target = e.target as HTMLSelectElement | HTMLInputElement
setFormState({
...formState,
[target.name]: target.value,
})
}
useEffect(() => {
onChange({ ...formState, type: 'dial' })
}, [formState])
return (
<>
<div className="input">
<label>Unit</label>
<input name="unit" value={formState.unit} onChange={handleChange} />
</div>
</>
)
}

View File

@ -5,6 +5,7 @@ import { useDashboardContext } from '../contexts/DashboardContext'
import { useDragging } from '../hooks/useDragging'
import { ResizingMode, useResize } from '../hooks/useResize'
import { BoxDefinition } from '../types'
import { BoxDialContent } from './BoxDialContent'
import { BoxGraphContent } from './BoxGraphContent'
import { BoxSettings } from './BoxSettings/BoxSettings'
@ -129,6 +130,9 @@ export const EditableBox = ({
{box.sensor && box.data?.type === 'graph' && (
<BoxGraphContent box={box} data={box.data} />
)}
{box.sensor && box.data?.type === 'dial' && (
<BoxDialContent box={box} data={box.data} />
)}
</div>
<div

View File

@ -24,7 +24,7 @@ export const useDragging = ({ cellWidth, boxes, box }: Props) => {
Math.min(GRID_WIDTH - box.w, Math.round(state.x / cellWidth))
)
const dragY = Math.round(state.y / GRID_H_SNAP) * GRID_H_SNAP
const dragY = Math.max(0, Math.round(state.y / GRID_H_SNAP) * GRID_H_SNAP)
const gridHeights = Array(GRID_WIDTH)
.fill(null)

View File

@ -11,7 +11,7 @@ export type DashboardContentBox = {
h: number
sensor?: string
title?: string
data?: DashboardGraphData
data?: DashboardGraphData | DashboardDialData
}
export type DashboardGraphData = {
@ -22,6 +22,11 @@ export type DashboardGraphData = {
graphType?: string
}
export type DashboardDialData = {
type: 'dial'
unit?: string
}
export const parseDashboard = (input: string) => {
return JSON.parse(input) as DashboardContent
}

View File

@ -11,11 +11,15 @@ type postSensorValueBody struct {
Value float64 `json:"value"`
}
type getSensorQuery struct {
type getSensorValuesQuery struct {
From int64 `form:"from"`
To int64 `form:"to"`
}
type getLatestSensorValueQuery struct {
To int64 `form:"to"`
}
func PostSensorValues(s *app.Server) gin.HandlerFunc {
return func(c *gin.Context) {
var newValue postSensorValueBody
@ -37,7 +41,7 @@ func PostSensorValues(s *app.Server) gin.HandlerFunc {
func GetSensorValues(s *app.Server) gin.HandlerFunc {
return func(c *gin.Context) {
var query getSensorQuery
var query getSensorValuesQuery
sensor := c.Param("sensor")
@ -58,7 +62,7 @@ func GetSensorValues(s *app.Server) gin.HandlerFunc {
func GetSensorLatestValue(s *app.Server) gin.HandlerFunc {
return func(c *gin.Context) {
var query getSensorQuery
var query getLatestSensorValueQuery
sensor := c.Param("sensor")
@ -67,7 +71,7 @@ func GetSensorLatestValue(s *app.Server) gin.HandlerFunc {
return
}
value, err := s.Services.SensorValues.GetLatest(sensor, query.From, query.To)
value, err := s.Services.SensorValues.GetLatest(sensor, query.To)
if err != nil {
c.AbortWithError(500, err)

View File

@ -51,10 +51,10 @@ func (s *SensorValuesService) GetList(sensor string, from int64, to int64) ([]se
return values, nil
}
func (s *SensorValuesService) GetLatest(sensor string, from int64, to int64) (*sensorValue, error) {
func (s *SensorValuesService) GetLatest(sensor string, to int64) (*sensorValue, error) {
var value = sensorValue{}
row := s.ctx.DB.QueryRow("SELECT timestamp, value FROM sensor_values WHERE sensor = ? AND timestamp > ? AND timestamp < ? ORDER BY timestamp DESC LIMIT 1", sensor, from, to)
row := s.ctx.DB.QueryRow("SELECT timestamp, value FROM sensor_values WHERE sensor = ? timestamp < ? ORDER BY timestamp DESC LIMIT 1", sensor, to)
err := row.Scan(&value.Timestamp, &value.Value)
if err != nil {