This commit is contained in:
Corey Johnson 2025-11-19 13:19:18 -08:00
parent dc902565f3
commit 781ad51d9b
4 changed files with 129 additions and 128 deletions

View File

@ -5,10 +5,12 @@ import { sleep } from "bun"
import { processStderr, processStdout } from "./utils/stdio"
import Buzz from "./buzz"
import { join } from "path"
import { GPIO } from "./pins"
import GPIO from "./pins"
import { Agent } from "./agent"
import { searchWeb } from "./agent/tools"
// TODO: Kill baresip process on exit
type CancelableTask = () => void
type PhoneContext = {
@ -80,58 +82,58 @@ if (!agentId) {
await startPhone(agentId, apiKey)
// log.info("📞 GPIO inputs initialized")
const startBaresip = async (hook: GPIO.InputPin) => {
// const baresipConfig = join(import.meta.dir, "..", "baresip")
// const baresip = new Baresip(["/usr/bin/baresip", "-v", "-f", baresipConfig])
// // const baresipConfig = join(import.meta.dir, "..", "baresip")
// // const baresip = new Baresip(["/usr/bin/baresip", "-v", "-f", baresipConfig])
// baresip.registrationSuccess.connect(async () => {
// log.info("🐻 server connected")
// const result = await gpio.get(pins.hook)
// if (result.state === "low") {
// phoneService.send({ type: "initialized" })
// } else {
// phoneService.send({ type: "pick_up" })
// }
// })
// // baresip.registrationSuccess.connect(async () => {
// // log.info("🐻 server connected")
// // const result = await gpio.get(pins.hook)
// // if (result.state === "low") {
// // phoneService.send({ type: "initialized" })
// // } else {
// // phoneService.send({ type: "pick_up" })
// // }
// // })
// baresip.callReceived.connect(({ contact }) => {
// log.info(`🐻 incoming call from ${contact}`)
// phoneService.send({ type: "incoming_call", from: contact })
// })
// // baresip.callReceived.connect(({ contact }) => {
// // log.info(`🐻 incoming call from ${contact}`)
// // phoneService.send({ type: "incoming_call", from: contact })
// // })
// baresip.callEstablished.connect(({ contact }) => {
// log.info(`🐻 call established with ${contact}`)
// phoneService.send({ type: "answered" })
// })
// // baresip.callEstablished.connect(({ contact }) => {
// // log.info(`🐻 call established with ${contact}`)
// // phoneService.send({ type: "answered" })
// // })
// baresip.hungUp.connect(() => {
// log.info("🐻 call hung up")
// phoneService.send({ type: "remote_hang_up" })
// })
// // baresip.hungUp.connect(() => {
// // log.info("🐻 call hung up")
// // phoneService.send({ type: "remote_hang_up" })
// // })
// baresip.connect().catch((error) => {
// log.error("🐻 connection error:", error)
// phoneService.send({ type: "error", message: error.message })
// })
// // baresip.connect().catch((error) => {
// // log.error("🐻 connection error:", error)
// // phoneService.send({ type: "error", message: error.message })
// // })
// baresip.error.connect(async ({ message }) => {
// log.error("🐻 error:", message)
// phoneService.send({ type: "error", message })
// for (let i = 0; i < 4; i++) {
// await ring(500)
// await sleep(250)
// }
// process.exit(1)
// })
// // baresip.error.connect(async ({ message }) => {
// // log.error("🐻 error:", message)
// // phoneService.send({ type: "error", message })
// // for (let i = 0; i < 4; i++) {
// // await ring(500)
// // await sleep(250)
// // }
// // process.exit(1)
// // })
// const agent = new Agent({
// agentId,
// apiKey,
// tools: {
// search_web: (args: { query: string }) => searchWeb(args.query),
// },
// })
// const agent = new Agent({
// agentId,
// apiKey,
// tools: {
// search_web: (args: { query: string }) => searchWeb(args.query),
// },
// })
}
// handleAgentEvents(agent)

View File

@ -1,65 +0,0 @@
import { readdir } from "node:fs/promises"
import { gpiod, cstr } from "./ffi"
import { Output } from "./output"
import { Input } from "./input"
import { ChipNotFoundError } from "./errors"
import type { OutputOptions, InputOptions, ChipInfo } from "./types"
export class GPIO {
#chipPath: string
constructor(options?: { chip?: string }) {
this.#chipPath = options?.chip ?? "/dev/gpiochip0"
}
output(pin: number, options?: OutputOptions): Output {
return new Output(this.#chipPath, pin, options)
}
input(pin: number, options?: InputOptions): Input {
return new Input(this.#chipPath, pin, options)
}
async listChips(): Promise<ChipInfo[]> {
const chips: ChipInfo[] = []
try {
const files = await readdir("/dev")
const chipFiles = files.filter((f) => f.startsWith("gpiochip"))
for (const file of chipFiles) {
const path = `/dev/${file}`
try {
const chip = gpiod.gpiod_chip_open(cstr(path))
if (!chip) continue
const info = gpiod.gpiod_chip_get_info(chip)
if (!info) {
gpiod.gpiod_chip_close(chip)
continue
}
const name = gpiod.gpiod_chip_info_get_name(info)
const label = gpiod.gpiod_chip_info_get_label(info)
const numLines = gpiod.gpiod_chip_info_get_num_lines(info)
chips.push({
path,
name: String(name || ""),
label: String(label || ""),
numLines: Number(numLines),
})
gpiod.gpiod_chip_close(chip)
} catch {
continue
}
}
} catch {
// /dev might not be accessible, return empty array
}
return chips
}
}

View File

@ -1,17 +1,87 @@
export { GPIO } from "./gpio"
export {
import { readdir } from "node:fs/promises"
import { gpiod, cstr } from "./ffi"
import { Output } from "./output"
import { Input } from "./input"
import type * as Type from "./types"
import {
GPIOError,
PermissionError,
PinInUseError,
ChipNotFoundError,
InvalidConfigError,
} from "./errors"
export type {
InputOptions,
OutputOptions,
InputEvent,
InputGroupEvent,
ChipInfo,
PullMode,
EdgeMode,
} from "./types"
class GPIO {
#chipPath: string
constructor(options?: { chip?: string }) {
this.#chipPath = options?.chip ?? "/dev/gpiochip0"
}
output(pin: number, options?: Type.OutputOptions): Output {
return new Output(this.#chipPath, pin, options)
}
input(pin: number, options?: Type.InputOptions): Input {
return new Input(this.#chipPath, pin, options)
}
async listChips(): Promise<Type.ChipInfo[]> {
const chips: Type.ChipInfo[] = []
try {
const files = await readdir("/dev")
const chipFiles = files.filter((f) => f.startsWith("gpiochip"))
for (const file of chipFiles) {
const path = `/dev/${file}`
try {
const chip = gpiod.gpiod_chip_open(cstr(path))
if (!chip) continue
const info = gpiod.gpiod_chip_get_info(chip)
if (!info) {
gpiod.gpiod_chip_close(chip)
continue
}
const name = gpiod.gpiod_chip_info_get_name(info)
const label = gpiod.gpiod_chip_info_get_label(info)
const numLines = gpiod.gpiod_chip_info_get_num_lines(info)
chips.push({
path,
name: String(name || ""),
label: String(label || ""),
numLines: Number(numLines),
})
gpiod.gpiod_chip_close(chip)
} catch {
continue
}
}
} catch {
// /dev might not be accessible, return empty array
}
return chips
}
static Error = GPIOError
static PermissionError = PermissionError
static PinInUseError = PinInUseError
static ChipNotFoundError = ChipNotFoundError
static InvalidConfigError = InvalidConfigError
}
namespace GPIO {
export type PullMode = Type.PullMode
export type EdgeMode = Type.EdgeMode
export type InputOptions = Type.InputOptions
export type OutputOptions = Type.OutputOptions
export type InputEvent = Type.InputEvent
}
export default GPIO

View File

@ -17,15 +17,9 @@ export type InputEvent = {
timestamp: bigint // nanoseconds
}
export type InputGroupEvent<T extends string = string> = InputEvent & {
pin: T // name of the pin that fired
}
export type ChipInfo = {
path: string
name: string
label: string
numLines: number
}
export type PinConfig = Record<string, { offset: number; pull: PullMode }>