All files / src/components/hooks useMqtt.js

0% Statements 0/49
0% Branches 0/34
0% Functions 0/7
0% Lines 0/43

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98                                                                                                                                                                                                   
import { useEffect, useState } from 'react'
import mqtt from 'mqtt'
 
const brokerUrl = 'wss://mqtt-ws.smartindustries.fontysict.net:443'
const client = mqtt.connect(brokerUrl)
 
// DEBUG to see if broker is connected
// client.on('connect', () => {
// 	console.log('Connected to MQTT broker')
// })
 
const useMqtt = (topic, debug = false) => {
	const [data, setData] = useState(null)
 
	useEffect(() => {
		client.subscribe(topic, (err) => {
			if (err) {
				if (debug) console.error('Subscription error:', err)
			} else {
				if (debug) console.log(`Subscribed to topic: ${topic}`)
			}
		})
 
		const handleMessage = (msgTopic, message) => {
			const payloadStr = message.toString()
			let parsed
 
			try {
				parsed = JSON.parse(payloadStr)
			} catch {
				parsed = isNaN(payloadStr) ? payloadStr : parseFloat(payloadStr)
			}
 
			const msgParts = msgTopic.split('/')
			const topicParts = topic.split('/')
 
			// Extract device ID from topic (if subscribing to one device)
			const subscribedToSingleDevice =
				topicParts.length === msgParts.length &&
				topic.endsWith('/#') &&
				msgParts.length >= 4 // e.g., /air-sensing/MAC/field
 
			// Smart handling: dynamic based on topic depth
			if (subscribedToSingleDevice) {
				// Collect individual fields directly (no MAC wrap)
				const field = msgParts[msgParts.length - 1]
 
				setData((prev = {}) => {
					const updated = {...prev, [field]: parsed}
					if (debug) console.log(`[Device Fields] ${msgTopic}:`, updated)
					return updated
				})
				return
			}
 
			// Full wildcard structure (e.g. air-sensing/# → multiple devices)
			if (topic.endsWith('/#')) {
				const deviceId = msgParts[2]
				const field = msgParts[3]
 
				setData((prev = {}) => {
					const updated = {
						...prev,
						[deviceId]: {
							...(prev?.[deviceId] || {}),
							...(field ? { [field]: parsed } : typeof parsed === 'object' ? parsed : {}),
						},
					}
					if (debug) console.log(`[Wildcard Devices] ${msgTopic}:`, updated)
					return updated
				})
				return
			}
 
			// Single value case (e.g., /.../humidity)
			if (msgParts.length >= 4) {
				if (debug) console.log(`[Single Field] ${msgTopic}:`, parsed)
				setData(parsed)
				return
			}
 
			// Default fallback
			if (debug) console.log(`[Default] ${msgTopic}:`, parsed)
			setData(parsed)
		}
 
		client.on('message', handleMessage)
 
		return () => {
			client.unsubscribe(topic)
			client.removeListener('message', handleMessage)
		}
	}, [topic])
 
	return data
}
 
export default useMqtt