82 lines
2.7 KiB
TypeScript
82 lines
2.7 KiB
TypeScript
import { HomeAssistant } from '../../ha/types';
|
|
import { FrigateEventChange, frigateEventChangeSchema } from './types';
|
|
|
|
export interface FrigateEventWatcherRequest {
|
|
instanceID: string;
|
|
matcher?(event: FrigateEventChange): boolean;
|
|
callback(event: FrigateEventChange): void;
|
|
}
|
|
|
|
export interface FrigateEventWatcherSubscriptionInterface {
|
|
subscribe(hass: HomeAssistant, request: FrigateEventWatcherRequest): Promise<void>;
|
|
unsubscribe(callback: FrigateEventWatcherRequest): void;
|
|
}
|
|
|
|
type SubscriptionUnsubscribe = () => Promise<void>;
|
|
|
|
export class FrigateEventWatcher implements FrigateEventWatcherSubscriptionInterface {
|
|
protected _requests: FrigateEventWatcherRequest[] = [];
|
|
protected _unsubscribeCallback: Record<string, SubscriptionUnsubscribe> = {};
|
|
|
|
public async subscribe(
|
|
hass: HomeAssistant,
|
|
request: FrigateEventWatcherRequest,
|
|
): Promise<void> {
|
|
const shouldSubscribe = !this._hasSubscribers(request.instanceID);
|
|
this._requests.push(request);
|
|
if (shouldSubscribe) {
|
|
this._unsubscribeCallback[request.instanceID] =
|
|
await hass.connection.subscribeMessage<string>(
|
|
(data) => this._receiveHandler(request.instanceID, data),
|
|
{ type: 'frigate/events/subscribe', instance_id: request.instanceID },
|
|
);
|
|
}
|
|
}
|
|
|
|
public async unsubscribe(request: FrigateEventWatcherRequest): Promise<void> {
|
|
this._requests = this._requests.filter(
|
|
(existingRequest) => existingRequest !== request,
|
|
);
|
|
|
|
if (!this._hasSubscribers(request.instanceID)) {
|
|
const callback = this._unsubscribeCallback[request.instanceID];
|
|
delete this._unsubscribeCallback[request.instanceID];
|
|
|
|
// Callback may be undefined if unsubscribe is called while subscribe is
|
|
// still awaiting the Home Assistant connection.
|
|
await callback?.();
|
|
}
|
|
}
|
|
|
|
protected _hasSubscribers(instanceID: string): boolean {
|
|
return !!this._requests.filter((request) => request.instanceID === instanceID)
|
|
.length;
|
|
}
|
|
|
|
protected _receiveHandler(instanceID: string, data: string): void {
|
|
let json: unknown;
|
|
try {
|
|
json = JSON.parse(data);
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
} catch (e) {
|
|
console.warn('Received non-JSON payload as Frigate event', data);
|
|
return;
|
|
}
|
|
|
|
const parsedEvent = frigateEventChangeSchema.safeParse(json);
|
|
if (!parsedEvent.success) {
|
|
console.warn('Received malformed Frigate event from Home Assistant', data);
|
|
return;
|
|
}
|
|
|
|
for (const request of this._requests) {
|
|
if (
|
|
request.instanceID === instanceID &&
|
|
(!request.matcher || request.matcher(parsedEvent.data))
|
|
) {
|
|
request.callback(parsedEvent.data);
|
|
}
|
|
}
|
|
}
|
|
}
|