| 1 | import * as Context from "effect/Context"; |
| 2 | import * as Effect from "effect/Effect"; |
| 3 | import * as Layer from "effect/Layer"; |
| 4 | import * as Scope from "effect/Scope"; |
| 5 | |
| 6 | import * as Electron from "electron"; |
| 7 | |
| 8 | export type ElectronThermalState = ReturnType<Electron.PowerMonitor["getCurrentThermalState"]>; |
| 9 | export type ElectronIdleState = ReturnType<Electron.PowerMonitor["getSystemIdleState"]>; |
| 10 | |
| 11 | export 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 | |
| 31 | const 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 | |
| 42 | const 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 | |
| 61 | const 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. */ |
| 79 | export 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 | |
| 90 | export const layer = Layer.succeed(ElectronPowerMonitor, make); |