1import * as Context from "effect/Context";
2import * as Effect from "effect/Effect";
3import * as Layer from "effect/Layer";
4import * as Scope from "effect/Scope";
5
6import * as Electron from "electron";
7
8export type ElectronThermalState = ReturnType<Electron.PowerMonitor["getCurrentThermalState"]>;
9export type ElectronIdleState = ReturnType<Electron.PowerMonitor["getSystemIdleState"]>;
10
11export class ElectronPowerMonitor extends Context.Service<
12 ElectronPowerMonitor,
13 {
14 readonly isOnBatteryPower: Effect.Effect<boolean>;
15 readonly getSystemIdleTime: Effect.Effect<number>;
16 readonly getSystemIdleState: (idleThresholdSeconds: number) => Effect.Effect<ElectronIdleState>;
17 readonly getCurrentThermalState: Effect.Effect<ElectronThermalState>;
18 readonly onSimpleEvent: (
19 eventName: "lock-screen" | "unlock-screen" | "on-ac" | "on-battery" | "suspend" | "resume",
20 listener: () => void,
21 ) => Effect.Effect<void, never, Scope.Scope>;
22 readonly onThermalStateChange: (
23 listener: (state: ElectronThermalState) => void,
24 ) => Effect.Effect<void, never, Scope.Scope>;
25 readonly onSpeedLimitChange: (
26 listener: (limit: number) => void,
27 ) => Effect.Effect<void, never, Scope.Scope>;
28 }
29>()("@t3tools/desktop/electron/ElectronPowerMonitor") {}
30
31const onSimpleEvent: ElectronPowerMonitor["Service"]["onSimpleEvent"] = (eventName, listener) =>
32 Effect.acquireRelease(
33 Effect.sync(() => {
34 Electron.powerMonitor.on(eventName as any, listener as any);
35 }),
36 () =>
37 Effect.sync(() => {
38 Electron.powerMonitor.removeListener(eventName as any, listener as any);
39 }),
40 ).pipe(Effect.asVoid);
41
42const onThermalStateChange: ElectronPowerMonitor["Service"]["onThermalStateChange"] = (
43 listener,
44) => {
45 const wrapped = (
46 event: Electron.Event<Electron.PowerMonitorThermalStateChangeEventParams>,
47 ): void => {
48 listener(event.state);
49 };
50 return Effect.acquireRelease(
51 Effect.sync(() => {
52 Electron.powerMonitor.on("thermal-state-change", wrapped);
53 }),
54 () =>
55 Effect.sync(() => {
56 Electron.powerMonitor.removeListener("thermal-state-change", wrapped);
57 }),
58 ).pipe(Effect.asVoid);
59};
60
61const onSpeedLimitChange: ElectronPowerMonitor["Service"]["onSpeedLimitChange"] = (listener) => {
62 const wrapped = (
63 event: Electron.Event<Electron.PowerMonitorSpeedLimitChangeEventParams>,
64 ): void => {
65 listener(event.limit);
66 };
67 return Effect.acquireRelease(
68 Effect.sync(() => {
69 Electron.powerMonitor.on("speed-limit-change", wrapped);
70 }),
71 () =>
72 Effect.sync(() => {
73 Electron.powerMonitor.removeListener("speed-limit-change", wrapped);
74 }),
75 ).pipe(Effect.asVoid);
76};
77
78/** @public Service construction is part of the canonical Effect module API. */
79export const make = ElectronPowerMonitor.of({
80 isOnBatteryPower: Effect.sync(() => Electron.powerMonitor.isOnBatteryPower()),
81 getSystemIdleTime: Effect.sync(() => Electron.powerMonitor.getSystemIdleTime()),
82 getSystemIdleState: (idleThresholdSeconds) =>
83 Effect.sync(() => Electron.powerMonitor.getSystemIdleState(idleThresholdSeconds)),
84 getCurrentThermalState: Effect.sync(() => Electron.powerMonitor.getCurrentThermalState()),
85 onSimpleEvent,
86 onThermalStateChange,
87 onSpeedLimitChange,
88});
89
90export const layer = Layer.succeed(ElectronPowerMonitor, make);