2021-02-16 05:10:20 +01:00
|
|
|
import { h, createContext } from 'preact';
|
|
|
|
import { baseUrl } from './baseUrl';
|
2023-05-05 14:45:13 +02:00
|
|
|
import { produce } from 'immer';
|
2021-02-16 05:10:20 +01:00
|
|
|
import { useCallback, useContext, useEffect, useRef, useReducer } from 'preact/hooks';
|
|
|
|
|
|
|
|
const initialState = Object.freeze({ __connected: false });
|
2022-11-24 03:03:20 +01:00
|
|
|
export const WS = createContext({ state: initialState, connection: null });
|
2021-02-16 05:10:20 +01:00
|
|
|
|
|
|
|
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,
|
|
|
|
};
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-24 03:03:20 +01:00
|
|
|
export function WsProvider({
|
2021-02-18 05:53:57 +01:00
|
|
|
config,
|
2021-02-16 05:10:20 +01:00
|
|
|
children,
|
|
|
|
createWebsocket = defaultCreateWebsocket,
|
2022-11-24 03:03:20 +01:00
|
|
|
wsUrl = `${baseUrl.replace(/^http/, 'ws')}ws`,
|
2021-02-16 05:10:20 +01:00
|
|
|
}) {
|
|
|
|
const [state, dispatch] = useReducer(reducer, initialState);
|
|
|
|
const wsRef = useRef();
|
|
|
|
|
2021-02-18 05:53:57 +01:00
|
|
|
useEffect(() => {
|
|
|
|
Object.keys(config.cameras).forEach((camera) => {
|
2023-07-01 15:18:33 +02:00
|
|
|
const { name, record, detect, snapshots, audio } = config.cameras[camera];
|
2022-04-25 13:59:52 +02:00
|
|
|
dispatch({ topic: `${name}/recordings/state`, payload: record.enabled ? 'ON' : 'OFF', retain: false });
|
|
|
|
dispatch({ topic: `${name}/detect/state`, payload: detect.enabled ? 'ON' : 'OFF', retain: false });
|
|
|
|
dispatch({ topic: `${name}/snapshots/state`, payload: snapshots.enabled ? 'ON' : 'OFF', retain: false });
|
2023-07-01 15:18:33 +02:00
|
|
|
dispatch({ topic: `${name}/audio/state`, payload: audio.enabled ? 'ON' : 'OFF', retain: false });
|
2021-02-18 05:53:57 +01:00
|
|
|
});
|
|
|
|
}, [config]);
|
|
|
|
|
2021-02-16 05:10:20 +01:00
|
|
|
useEffect(
|
|
|
|
() => {
|
2022-11-24 03:03:20 +01:00
|
|
|
const ws = createWebsocket(wsUrl);
|
2021-02-16 05:10:20 +01:00
|
|
|
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
|
2022-11-24 03:03:20 +01:00
|
|
|
[state.__reconnectAttempts, wsUrl] // eslint-disable-line react-hooks/exhaustive-deps
|
2021-02-16 05:10:20 +01:00
|
|
|
);
|
|
|
|
|
2022-11-24 03:03:20 +01:00
|
|
|
return <WS.Provider value={{ state, ws: wsRef.current }}>{children}</WS.Provider>;
|
2021-02-16 05:10:20 +01:00
|
|
|
}
|
|
|
|
|
2022-11-24 03:03:20 +01:00
|
|
|
export function useWs(watchTopic, publishTopic) {
|
|
|
|
const { state, ws } = useContext(WS);
|
2021-02-16 05:10:20 +01:00
|
|
|
|
2021-06-23 15:48:56 +02:00
|
|
|
const value = state[watchTopic] || { payload: null };
|
2021-02-16 05:10:20 +01:00
|
|
|
|
|
|
|
const send = useCallback(
|
2022-04-25 13:59:52 +02:00
|
|
|
(payload, retain = false) => {
|
2021-02-18 05:53:57 +01:00
|
|
|
ws.send(
|
|
|
|
JSON.stringify({
|
|
|
|
topic: publishTopic || watchTopic,
|
|
|
|
payload: typeof payload !== 'string' ? JSON.stringify(payload) : payload,
|
2022-04-25 13:59:52 +02:00
|
|
|
retain,
|
2021-02-18 05:53:57 +01:00
|
|
|
})
|
|
|
|
);
|
2021-02-16 05:10:20 +01:00
|
|
|
},
|
2021-07-06 14:22:48 +02:00
|
|
|
[ws, watchTopic, publishTopic]
|
2021-02-16 05:10:20 +01:00
|
|
|
);
|
|
|
|
|
|
|
|
return { value, send, connected: state.__connected };
|
|
|
|
}
|
2021-02-18 05:53:57 +01:00
|
|
|
|
|
|
|
export function useDetectState(camera) {
|
|
|
|
const {
|
|
|
|
value: { payload },
|
|
|
|
send,
|
|
|
|
connected,
|
2022-11-24 03:03:20 +01:00
|
|
|
} = useWs(`${camera}/detect/state`, `${camera}/detect/set`);
|
2021-02-18 05:53:57 +01:00
|
|
|
return { payload, send, connected };
|
|
|
|
}
|
|
|
|
|
2021-07-11 21:49:10 +02:00
|
|
|
export function useRecordingsState(camera) {
|
2021-02-18 05:53:57 +01:00
|
|
|
const {
|
|
|
|
value: { payload },
|
|
|
|
send,
|
|
|
|
connected,
|
2022-11-24 03:03:20 +01:00
|
|
|
} = useWs(`${camera}/recordings/state`, `${camera}/recordings/set`);
|
2021-02-18 05:53:57 +01:00
|
|
|
return { payload, send, connected };
|
|
|
|
}
|
|
|
|
|
|
|
|
export function useSnapshotsState(camera) {
|
|
|
|
const {
|
|
|
|
value: { payload },
|
|
|
|
send,
|
|
|
|
connected,
|
2022-11-24 03:03:20 +01:00
|
|
|
} = useWs(`${camera}/snapshots/state`, `${camera}/snapshots/set`);
|
2021-02-18 05:53:57 +01:00
|
|
|
return { payload, send, connected };
|
|
|
|
}
|
2021-06-21 17:07:14 +02:00
|
|
|
|
2023-07-01 15:18:33 +02:00
|
|
|
export function useAudioState(camera) {
|
|
|
|
const {
|
|
|
|
value: { payload },
|
|
|
|
send,
|
|
|
|
connected,
|
|
|
|
} = useWs(`${camera}/audio/state`, `${camera}/audio/set`);
|
|
|
|
return { payload, send, connected };
|
|
|
|
}
|
|
|
|
|
2023-04-26 13:08:53 +02:00
|
|
|
export function usePtzCommand(camera) {
|
|
|
|
const {
|
|
|
|
value: { payload },
|
|
|
|
send,
|
|
|
|
connected,
|
|
|
|
} = useWs(`${camera}/ptz`, `${camera}/ptz`);
|
|
|
|
return { payload, send, connected };
|
|
|
|
}
|
|
|
|
|
2021-06-21 17:07:14 +02:00
|
|
|
export function useRestart() {
|
|
|
|
const {
|
2021-07-06 14:22:48 +02:00
|
|
|
value: { payload },
|
2021-06-21 17:07:14 +02:00
|
|
|
send,
|
|
|
|
connected,
|
2022-11-24 03:03:20 +01:00
|
|
|
} = useWs('restart', 'restart');
|
2021-07-06 14:22:48 +02:00
|
|
|
return { payload, send, connected };
|
2021-06-21 17:07:14 +02:00
|
|
|
}
|