Files
scrypted/server/src/event-registry.ts
2022-12-30 20:52:05 -08:00

105 lines
3.6 KiB
TypeScript

import { EventDetails, EventListenerOptions, EventListenerRegister, ScryptedInterface } from "@scrypted/types";
export class EventListenerRegisterImpl implements EventListenerRegister {
removeListener: () => void;
constructor(removeListener: () => void) {
this.removeListener = removeListener;
}
}
export function getMixinEventName(options: string | EventListenerOptions) {
let { event, mixinId } = (options || {}) as EventListenerOptions;
if (!event && typeof options === 'string')
event = options as string;
if (!event)
event = undefined;
if (!mixinId)
return event;
let ret = `${event}-mixin-${mixinId}`;
return ret;
}
// todo: storage should only go to host plugin
const allowedEventInterfaces = new Set<string>([ScryptedInterface.ScryptedDevice, 'Logger', 'Storage'])
export class EventRegistry {
systemListeners = new Set<(id: string, eventDetails: EventDetails, eventData: any) => void>();
listeners: { [token: string]: Set<(eventDetails: EventDetails, eventData: any) => void> } = {};
listen(callback: (id: string, eventDetails: EventDetails, eventData: any) => void): EventListenerRegister {
const events = this.systemListeners;
events.add(callback);
return new EventListenerRegisterImpl(() => {
events.delete(callback);
callback = undefined;
});
}
listenDevice(id: string, options: string | EventListenerOptions, callback: (eventDetails: EventDetails, eventData: any) => void): EventListenerRegister {
let event = getMixinEventName(options)
const token = `${id}#${event}`;
let events = this.listeners[token];
if (!events) {
events = new Set();
this.listeners[token] = events;
}
events.add(callback);
return new EventListenerRegisterImpl(() => {
events.delete(callback);
callback = undefined;
});
}
notify(id: string | undefined, eventTime: number, eventInterface: string, property: string | undefined, value: any, options?: {
changed?: boolean;
mixinId?: string;
}): boolean {
const { changed, mixinId } = options || {};
// prevent property event noise
if (property && !changed)
return false;
const eventDetails: EventDetails = {
eventId: undefined,
eventInterface,
eventTime,
property,
mixinId,
};
return this.notifyEventDetails(id, eventDetails, value);
}
notifyEventDetails(id: string | undefined, eventDetails: EventDetails, value: any, eventInterface?: string) {
eventDetails.eventId ||= Math.random().toString(36).substring(2);
eventInterface ||= eventDetails.eventInterface;
// system listeners only get state changes.
// there are many potentially noisy stateless events, like
// object detection and settings changes.
if ((eventDetails.property && !eventDetails.mixinId) || allowedEventInterfaces.has(eventInterface)) {
for (const event of this.systemListeners) {
event(id, eventDetails, value);
}
}
const events = this.listeners[`${id}#${eventInterface}`];
if (events) {
for (const event of events) {
event(eventDetails, value);
}
}
const allEvents = this.listeners[`${id}#${undefined}`];
if (allEvents) {
for (const event of allEvents) {
event(eventDetails, value);
}
}
return true;
}
}