• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

atomic14 / web-serial-plotter / 19188926463

08 Nov 2025 05:57AM UTC coverage: 59.262% (-1.9%) from 61.184%
19188926463

Pull #24

github

web-flow
Merge 947b7e7dd into ae73642b7
Pull Request #24: Feature: HTTP and WebSocket data source support

424 of 545 branches covered (77.8%)

Branch coverage included in aggregate %.

63 of 227 new or added lines in 5 files covered. (27.75%)

2097 of 3709 relevant lines covered (56.54%)

32.19 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

58.09
/src/hooks/useDataConnection.ts
1
import { useCallback, useState, useEffect } from 'react'
1✔
2
import { useSerial } from './useSerial'
1✔
3
import { useSignalGenerator, type GeneratorConfig } from './useSignalGenerator'
1✔
4
import { useHttp } from './useHttp'
1✔
5

6
export interface SerialConfig {
7
  baudRate: number
8
  dataBits: 5 | 6 | 7 | 8
9
  stopBits: 1 | 2
10
  parity: 'none' | 'even' | 'odd'
11
  flowControl: 'none' | 'hardware'
12
}
13

14
export type ConnectionType = 'serial' | 'http' | 'generator'
15

16
export interface ConnectionState {
17
  type: ConnectionType | null
18
  isConnecting: boolean
19
  isConnected: boolean
20
  isSupported: boolean
21
  error: string | null
22
}
23

24
export interface UseDataConnection {
25
  state: ConnectionState
26
  connectSerial: (config: SerialConfig) => Promise<void>
27
  connectHttp: (address: string) => Promise<void>
28
  connectGenerator: (config: GeneratorConfig) => Promise<void>
29
  disconnect: () => Promise<void>
30
  write: (data: string) => Promise<void>
31
  generatorConfig: GeneratorConfig
32
  setGeneratorConfig: (config: Partial<GeneratorConfig>) => void
33
}
34

35
const DEFAULT_SERIAL_CONFIG: SerialConfig = {
1✔
36
  baudRate: 115200,
1✔
37
  dataBits: 8,
1✔
38
  stopBits: 1,
1✔
39
  parity: 'none',
1✔
40
  flowControl: 'none'
1✔
41
}
1✔
42

43
export function useDataConnection(onLine: (line: string) => void): UseDataConnection {
1✔
44
  const [connectionType, setConnectionType] = useState<ConnectionType | null>(null)
6✔
45
  const [isConnecting, setIsConnecting] = useState(false)
6✔
46
  const [error, setError] = useState<string | null>(null)
6✔
47

48
  const serial = useSerial()
6✔
49
  const generator = useSignalGenerator(onLine)
6✔
50
  const http = useHttp()
6✔
51

52
  const state: ConnectionState = {
6✔
53
    type: connectionType,
6✔
54
    isConnecting: isConnecting || serial.state.isConnecting || http.state.isConnecting,
6✔
55
    isConnected: serial.state.isConnected || generator.isRunning || http.state.isConnected,
6✔
56
    isSupported: serial.state.isSupported && http.state.isSupported,
6✔
57
    error: error || serial.state.error || http.state.error
6✔
58
  }
6✔
59

60
  const connectSerial = useCallback(async (config: SerialConfig) => {
6✔
61
    if (generator.isRunning) {
2!
62
      generator.stop()
×
63
    }
×
64

65
    if (http.state.isConnected) {
2!
NEW
66
      await http.disconnect()
×
NEW
67
    }
×
68
    
69
    setIsConnecting(true)
2✔
70
    setError(null)
2✔
71
    
72
    try {
2✔
73
      // Convert our config to the format useSerial expects
74
      // Note: Web Serial API has limited configuration options
75
      await serial.connect(config.baudRate)
2✔
76
      setConnectionType('serial')
2✔
77
      // The actual port configuration would need to be done at the port.open() level
78
      // For now, we'll just use baudRate as useSerial currently does
79
    } catch (err) {
2!
80
      const message = err instanceof Error ? err.message : 'Failed to connect to serial port'
×
81
      setError(message)
×
82
      setConnectionType(null)
×
83
      throw err // Re-throw so ConnectModal knows the connection failed
×
84
    } finally {
2✔
85
      setIsConnecting(false)
2✔
86
    }
2✔
87
  }, [serial, generator, http])
6✔
88

89
  const connectHttp = useCallback(async (address: string) => {
6✔
NEW
90
    if (serial.state.isConnected) {
×
NEW
91
      await serial.disconnect()
×
NEW
92
    }
×
93

NEW
94
    if (generator.isRunning) {
×
NEW
95
      generator.stop()
×
NEW
96
    }
×
97
    
NEW
98
    setIsConnecting(true)
×
NEW
99
    setError(null)
×
100
    
NEW
101
    try {
×
NEW
102
      await http.connect(address)
×
NEW
103
      setConnectionType('http')
×
NEW
104
    } catch (err) {
×
NEW
105
      const message = err instanceof Error ? err.message : 'Failed to connect to HTTP stream'
×
NEW
106
      setError(message)
×
NEW
107
      setConnectionType(null)
×
NEW
108
      throw err
×
NEW
109
    } finally {
×
NEW
110
      setIsConnecting(false)
×
NEW
111
    }
×
112
  }, [serial, generator, http])
6✔
113

114
  const connectGenerator = useCallback(async (config: GeneratorConfig) => {
6✔
115
    if (serial.state.isConnected) {
1!
116
      await serial.disconnect()
×
117
    }
×
118

119
    if (http.state.isConnected) {
1!
NEW
120
      await http.disconnect()
×
NEW
121
    }
×
122
    
123
    setError(null)
1✔
124
    setConnectionType('generator')
1✔
125
    
126
    try {
1✔
127
      generator.setConfig(config)
1✔
128
      generator.start()
1✔
129
    } catch (err) {
1!
130
      const message = err instanceof Error ? err.message : 'Failed to start signal generator'
×
131
      setError(message)
×
132
      setConnectionType(null)
×
133
    }
×
134
  }, [serial, generator, http])
6✔
135

136
  const disconnect = useCallback(async () => {
6✔
137
    setError(null)
1✔
138
    
139
    if (serial.state.isConnected) {
1!
140
      await serial.disconnect()
×
141
    }
×
142
    
143
    if (generator.isRunning) {
1!
144
      generator.stop()
×
145
    }
×
146

147
    if (http.state.isConnected) {
1!
NEW
148
      await http.disconnect()
×
NEW
149
    }
×
150
    
151
    setConnectionType(null)
1✔
152
  }, [serial, generator, http])
6✔
153

154
  // Set up serial and http line handlers
155
  useEffect(() => {
6✔
156
    serial.onLine(onLine)
6✔
157
    http.onLine(onLine)
6✔
158
  }, [serial, http, onLine])
6✔
159

160
  const write = useCallback(async (data: string) => {
6✔
NEW
161
    if (connectionType === 'serial' && serial.state.isConnected) {
×
NEW
162
      await serial.write(data)
×
NEW
163
    } else if (connectionType === 'http' && http.state.isConnected) {
×
NEW
164
      await http.write(data)
×
NEW
165
    } else {
×
NEW
166
      throw new Error('Not connected')
×
167
    }
×
168
  }, [connectionType, serial, http])
6✔
169

170
  return {
6✔
171
    state,
6✔
172
    connectSerial,
6✔
173
    connectHttp,
6✔
174
    connectGenerator,
6✔
175
    disconnect,
6✔
176
    write,
6✔
177
    generatorConfig: generator.config,
6✔
178
    setGeneratorConfig: generator.setConfig
6✔
179
  }
6✔
180
}
6✔
181

182
export { DEFAULT_SERIAL_CONFIG }
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc