diff --git a/package.json b/package.json index 969a0cf..7c6092b 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ }, "scripts": { "build:scss": "npx sass src/static/css:dist/static/css", - "copy:html": "cp src/static/index.html dist/static", + "copy:html": "cp src/static/*.* dist/static", "build:js:fe": "npx tsc src/static/js/*.ts --outDir dist/static/js", "build:js:be": "npx tsc --skipLibCheck src/*.ts --outDir dist", "build": "npm run build:js:fe && npm run build:js:be && npm run build:scss && npm run copy:html", diff --git a/src/http.ts b/src/http.ts index 67047a6..5f0d576 100644 --- a/src/http.ts +++ b/src/http.ts @@ -10,7 +10,7 @@ import IO from "./io"; private port: number; private root: string; - public constructor(port: number, root: string, io: IO) { + public constructor(port: number, root: string, test: (device:string)=>void) { this.port = port; this.root = root; this.httpServer = http.createServer((req, res) => { @@ -25,29 +25,16 @@ import IO from "./io"; switch (req.method) { case "GET": switch (api) { - case "sensors": - body = JSON.stringify(io.getSensors()); - status = 200; - break; - case "signal": - const adapter = parseInt(url.searchParams.get('adapter')); - // body = JSON.stringify(getSignal(adapter)); - status = 200; - break; - } break; case "PUT": switch (api) { - case "power": - const channel = decodeURIComponent(query[3]); - const adapter = parseInt(url.searchParams.get('adapter')); - // tune(channel, adapter); + case "test": + const device: string = query[3]; + test(device); status = 202; break; - } - } } else if (req.method === 'GET') { diff --git a/src/io.ts b/src/io.ts index 1c6706c..9e88a5a 100644 --- a/src/io.ts +++ b/src/io.ts @@ -17,7 +17,7 @@ export interface ISensors { humidity: number; } -export default class IO { +export default class IO { gpioLights: pigpio.Gpio gpioHeat: pigpio.Gpio serial: Serial; @@ -25,7 +25,7 @@ export default class IO { heat: boolean; getMoisture: () => ({ moisture0: number, moisture1: number }); - public constructor(LIGHTS_GPIO = 17, HEAT_GPIO=22, DHT_GPIO = 27, DHT_MODEL = 22) { + public constructor(LIGHTS_GPIO = 17, HEAT_GPIO = 22, DHT_GPIO = 27, DHT_MODEL = 22) { const serial = new Serial() this.getMoisture = serial.getMoisture.bind(serial); this.serial = serial; @@ -43,23 +43,18 @@ export default class IO { dht.setMaxRetries(10); } - public setPower(state: boolean, GPIO= 17) { - if(GPIO == 17){ + public setPower(state: boolean, GPIO = 17) { + if (GPIO == 17) { this.lights = state; this.gpioLights.digitalWrite(this.lights ? ON : OFF); } - else if(GPIO == 22){ + else if (GPIO == 22) { this.heat = state; this.gpioHeat.digitalWrite(this.heat ? ON : OFF); } } - // public togglePower() { - // this.power = !this.power; - // this.gpio.digitalWrite(this.power ? ON : OFF); - // } - private round = (n) => (n * 100) / 100 diff --git a/src/programs.json b/src/programs.json new file mode 100644 index 0000000..0d90b40 --- /dev/null +++ b/src/programs.json @@ -0,0 +1,7 @@ +[ + { + "name": "tomato", + "daylightHours": 16, + "soilMoisture": 75 + } +] \ No newline at end of file diff --git a/src/server.ts b/src/server.ts index d99eedb..63384f6 100644 --- a/src/server.ts +++ b/src/server.ts @@ -1,17 +1,55 @@ import HttpServer from './http'; import IO, { ISensors } from './io'; import VideoSocket from './ws'; +import * as programs from './programs.json' const HTTP_PORT = process.env.HTTP_PORT ? parseInt(process.env.HTTP_PORT, 10) : 8080; const WS_PORT = process.env.WS_PORT ? parseInt(process.env.WS_PORT, 10) : 3003; const STATIC_ROOT = process.cwd() + "/dist/static"; const TV_DEV_0 = process.env.TV_DEV_0 ?? '/dev/video0' +const GPIO_LIGHTS = parseInt(process.env.GPIO_LIGHTS) ?? 17; +const GPIO_HEAT = parseInt(process.env.GPIO_HEAT) ?? 22; +const START_HOUR = parseInt(process.env.START_HOUR) ?? 8; +interface IProgram { + name:string, + daylightHours: number, + soilMoisture: number +} const io = new IO(); -const getSensors: ()=>ISensors = io.getSensors.bind(io); +const getSensors: () => ISensors = io.getSensors.bind(io); -const httpServer = new HttpServer(HTTP_PORT, STATIC_ROOT, io); +const test = (device: string) => { + const gpio = device == "lights" ? GPIO_LIGHTS : device == "heat" ? GPIO_HEAT : NaN + if (!isNaN(gpio)) { + const state = io[device]; + io.setPower(!state, gpio) + setTimeout(() => { + io.setPower(state, gpio) + }, 4000) + } + +} + +const runProgram = async (ID = 0) =>{ + let state = false; + const program: IProgram = programs[ID]; + const {daylightHours, soilMoisture} = program; + const now = new Date(); + const startTime = new Date(); + startTime.setHours(START_HOUR, 0, 0, 0); + const endTime = new Date(startTime.getTime()); + endTime.setHours(startTime.getHours() + daylightHours); + if(now >= startTime && now <= endTime){ + state = true; + } + io.setPower(state,GPIO_LIGHTS); + io.setPower(state,GPIO_HEAT); +} +runProgram(); + +const httpServer = new HttpServer(HTTP_PORT, STATIC_ROOT, test); const videoSocket = new VideoSocket(WS_PORT, TV_DEV_0, getSensors); httpServer.start(); @@ -26,31 +64,28 @@ process.stdin.on("data", async (data: string) => { const input = data.trim(); console.log(`Received: "${input}"`); const val = parseInt(input); - switch(val) { + switch (val) { case 0: - break; + break; case 1: - io.setPower(false,17); + io.setPower(false, GPIO_LIGHTS); break; case 2: - io.setPower(true,17); + io.setPower(true, GPIO_LIGHTS); break; case 3: - io.setPower(false,22); + io.setPower(false, GPIO_HEAT); break; case 4: - io.setPower(true,22); + io.setPower(true, GPIO_HEAT); break; - - case 5: - console.log("a ", getSensors()) + console.log(getSensors()) break; default: - console.log("No option for "+input) + console.log("No option for " + input) } - }); diff --git a/src/static/css/styes.scss b/src/static/css/styles.scss similarity index 100% rename from src/static/css/styes.scss rename to src/static/css/styles.scss diff --git a/src/static/favicon.ico b/src/static/favicon.ico new file mode 100644 index 0000000..81b6bd8 Binary files /dev/null and b/src/static/favicon.ico differ diff --git a/src/static/index.html b/src/static/index.html index 101f432..d31f62a 100644 --- a/src/static/index.html +++ b/src/static/index.html @@ -2,7 +2,7 @@ - WebRTC Stream + Grow @@ -21,6 +21,7 @@

--> +
diff --git a/src/static/js/api.ts b/src/static/js/api.ts index ac8ebc9..e15359d 100644 --- a/src/static/js/api.ts +++ b/src/static/js/api.ts @@ -1,9 +1,4 @@ -const getSensors = () =>{ +const test = (gpio: string ) => { + fetch(`/api/test/${gpio}`, { method: "PUT"}) +}; -} - -const poll = () =>{ - fetch("/api/sensors").then(r=>r.json()).then(data =>{ - console.log("data ",data) - }) -} \ No newline at end of file diff --git a/src/ws.ts b/src/ws.ts index 017fdef..09176e9 100644 --- a/src/ws.ts +++ b/src/ws.ts @@ -51,7 +51,7 @@ export default class VideoSocket { }; dataChannel.onmessage = (event) => { - console.log("📦 Server received message:", event.data); + // console.log("📦 Server received message:", event.data); }; dataChannel.onclose = () => { diff --git a/tsconfig.json b/tsconfig.json index 633cb3e..76fc191 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -44,7 +44,7 @@ // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ // "noUncheckedSideEffectImports": true, /* Check side effect imports. */ - // "resolveJsonModule": true, /* Enable importing .json files. */ + "resolveJsonModule": true, /* Enable importing .json files. */ // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ /* JavaScript Support */ @@ -106,7 +106,7 @@ "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ "skipLibCheck": true /* Skip type checking all .d.ts files. */ }, - "include": ["src"], + "include": ["src/**/*.ts"], "exclude": [ "node_modules","./node_modules/@roamhq/wrtc" ]