123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129 |
- import { h, createContext } from 'preact';
- import { baseUrl } from './baseUrl';
- import produce from 'immer';
- import { useCallback, useContext, useEffect, useRef, useReducer } from 'preact/hooks';
- const initialState = Object.freeze({ __connected: false });
- export const Mqtt = createContext({ state: initialState, connection: null });
- const defaultCreateWebsocket = (url) => new WebSocket(url);
- function reducer(state, { topic, payload, retain }) {
- switch (topic) {
- case '__CLIENT_CONNECTED':
- return produce(state, (draftState) => {
- draftState.__connected = true;
- });
- default:
- return produce(state, (draftState) => {
- let parsedPayload = payload;
- try {
- parsedPayload = payload && JSON.parse(payload);
- } catch (e) {}
- draftState[topic] = {
- lastUpdate: Date.now(),
- payload: parsedPayload,
- retain,
- };
- });
- }
- }
- export function MqttProvider({
- config,
- children,
- createWebsocket = defaultCreateWebsocket,
- mqttUrl = `${baseUrl.replace(/^http/, 'ws')}/ws`,
- }) {
- const [state, dispatch] = useReducer(reducer, initialState);
- const wsRef = useRef();
- useEffect(() => {
- Object.keys(config.cameras).forEach((camera) => {
- const { name, clips, detect, snapshots } = config.cameras[camera];
- dispatch({ topic: `${name}/clips/state`, payload: clips.enabled ? 'ON' : 'OFF' });
- dispatch({ topic: `${name}/detect/state`, payload: detect.enabled ? 'ON' : 'OFF' });
- dispatch({ topic: `${name}/snapshots/state`, payload: snapshots.enabled ? 'ON' : 'OFF' });
- });
- }, [config]);
- useEffect(
- () => {
- const ws = createWebsocket(mqttUrl);
- ws.onopen = () => {
- dispatch({ topic: '__CLIENT_CONNECTED' });
- };
- ws.onmessage = (event) => {
- dispatch(JSON.parse(event.data));
- };
- wsRef.current = ws;
- return () => {
- ws.close(3000, 'Provider destroyed');
- };
- },
- // Forces reconnecting
- [state.__reconnectAttempts, mqttUrl] // eslint-disable-line react-hooks/exhaustive-deps
- );
- return <Mqtt.Provider value={{ state, ws: wsRef.current }}>{children}</Mqtt.Provider>;
- }
- export function useMqtt(watchTopic, publishTopic) {
- const { state, ws } = useContext(Mqtt);
- const value = state[watchTopic] || { payload: null };
- const send = useCallback(
- (payload) => {
- ws.send(
- JSON.stringify({
- topic: publishTopic || watchTopic,
- payload: typeof payload !== 'string' ? JSON.stringify(payload) : payload,
- })
- );
- },
- [ws, watchTopic, publishTopic]
- );
- return { value, send, connected: state.__connected };
- }
- export function useDetectState(camera) {
- const {
- value: { payload },
- send,
- connected,
- } = useMqtt(`${camera}/detect/state`, `${camera}/detect/set`);
- return { payload, send, connected };
- }
- export function useClipsState(camera) {
- const {
- value: { payload },
- send,
- connected,
- } = useMqtt(`${camera}/clips/state`, `${camera}/clips/set`);
- return { payload, send, connected };
- }
- export function useSnapshotsState(camera) {
- const {
- value: { payload },
- send,
- connected,
- } = useMqtt(`${camera}/snapshots/state`, `${camera}/snapshots/set`);
- return { payload, send, connected };
- }
- export function useRestart() {
- const {
- value: { payload },
- send,
- connected,
- } = useMqtt('restart', 'restart');
- return { payload, send, connected };
- }
|