update
Took 15 hours 7 minutes
This commit is contained in:
parent
a356b39bad
commit
c81713cd23
39
doc/Notes.md
Normal file
39
doc/Notes.md
Normal file
@ -0,0 +1,39 @@
|
||||
# Notes und kleine Dokumentation
|
||||
Was haben wir bereits am iTender Projekt gemacht?
|
||||
|
||||
<hr>
|
||||
|
||||
## Konzept-Erstellung
|
||||
|
||||
#### Ideen
|
||||
- Grund-Ideen
|
||||
- Smarten Cocktail-Mischer
|
||||
- 4 Getränke Behälter (mit Saft, Sirup oder Likör bzw. Schnapps)
|
||||
- 4 Pumpen (Peristaltik Pumpe)
|
||||
- Raspberry Pi als Prozessoreinheit
|
||||
- Display in der Front mit Benutzeroberfläche
|
||||
- Automatisches filtern von Getränken, je nachdem welche "Zutaten" in den Behältern sind
|
||||
- Messung der aktuellen Füllmenge der Behälter, basierend auf Gewicht (mittels Wägezelle) oder Abstand zur Wasseroberfläche (mittels Ultraschall-Sensor)
|
||||
|
||||
- Nice to have
|
||||
- LED-Stripes für schöne Beleuchtung, basierend auf dem aktuellen Status der Maschine
|
||||
- Extra Schlauch für weitere außenstehende Getränke
|
||||
- Mit Bier-Fass Adapter?
|
||||
- Kühlung der Container mittels Peltierelement und Lüftern
|
||||
|
||||
#### Erstes 3D-Modell
|
||||
<img src="./Screenshot_Model1.1_FrontTopRight.png" width="50%">
|
||||
<img src="./Screenshot_Model1.1_BackDownLeft.png" width="50%">
|
||||
|
||||
#### Neues 3D-Modell
|
||||
Folgt.
|
||||
|
||||
<hr>
|
||||
|
||||
## Umsetzung
|
||||
|
||||
#### Webseiten-Skizzen
|
||||
|
||||
|
||||
|
||||
|
BIN
doc/Screenshot_Model1.1_BackDownLeft.png
Normal file
BIN
doc/Screenshot_Model1.1_BackDownLeft.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 102 KiB |
BIN
doc/Screenshot_Model1.1_FrontTopRight.png
Normal file
BIN
doc/Screenshot_Model1.1_FrontTopRight.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 108 KiB |
@ -5,7 +5,8 @@
|
||||
"author": "Tobias Hopp <tobi@gaminggeneration.de>",
|
||||
"license": "UNLICENSED",
|
||||
"scripts": {
|
||||
"start": "node ./dist/main.js",
|
||||
"boot": "yarn && yarn run compile && ./startFrontend.sh && yarn run start",
|
||||
"start": "DEBUG=itender:* node ./dist/main.js",
|
||||
"compile": "tsc && webpack",
|
||||
"compileStart": "yarn run compile; yarn start",
|
||||
"watchTS": "tsc --watch",
|
||||
@ -20,6 +21,7 @@
|
||||
"@types/morgan": "^1.9.3",
|
||||
"@types/node": "^18.11.9",
|
||||
"@types/rpi-gpio": "^2.1.1",
|
||||
"@types/rpi-ws281x-native": "^1.0.0",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"debug": "^4.3.4",
|
||||
"express": "~4.16.1",
|
||||
@ -28,11 +30,12 @@
|
||||
"http-errors": "~1.6.3",
|
||||
"mongoose": "^6.7.2",
|
||||
"morgan": "^1.10.0",
|
||||
"net-ping": "^1.2.3",
|
||||
"pug": "2.0.0-beta11",
|
||||
"rpi-gpio": "^2.1.7"
|
||||
"rpi-gpio": "^2.1.7",
|
||||
"rpi-ws281x-native": "^1.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/electron": "^1.6.10",
|
||||
"nodemon": "^2.0.20",
|
||||
"ts-loader": "^9.4.1",
|
||||
"ts-node": "^10.9.1",
|
||||
|
@ -1,10 +1,19 @@
|
||||
.btn {
|
||||
padding: 11px 15px;
|
||||
padding: 12px 16px;
|
||||
border: none;
|
||||
color: white;
|
||||
font-size: 0.8em;
|
||||
font-size: 0.95em;
|
||||
border-radius: 2px;
|
||||
cursor:none !important;
|
||||
cursor: none !important;
|
||||
|
||||
}
|
||||
|
||||
.inputGroup {
|
||||
margin-bottom: 1.5%;
|
||||
}
|
||||
|
||||
label {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
|
||||
@ -36,10 +45,12 @@
|
||||
color: white;
|
||||
}
|
||||
|
||||
|
||||
.btn-secondary:hover {
|
||||
background-color: var(--secondary-bright);
|
||||
}
|
||||
|
||||
|
||||
.btn-success {
|
||||
background-color: var(--success);
|
||||
color: white;
|
||||
@ -82,3 +93,27 @@
|
||||
.btn-dark:hover {
|
||||
background-color: var(--dark-bright);
|
||||
}
|
||||
|
||||
|
||||
|
||||
.input {
|
||||
padding: 6px;
|
||||
font-size: 1.1em;
|
||||
|
||||
margin-left: 1%;
|
||||
margin-right: 1%;
|
||||
border: 0;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
|
||||
.input[type=checkbox] {
|
||||
margin-bottom: 2px;
|
||||
/* Double-sized Checkboxes */
|
||||
-ms-transform: scale(1.5); /* IE */
|
||||
-moz-transform: scale(1.5); /* FF */
|
||||
-webkit-transform: scale(1.5); /* Safari and Chrome */
|
||||
-o-transform: scale(1.5); /* Opera */
|
||||
transform: scale(1.5);
|
||||
padding: 10px;
|
||||
}
|
58
public/stylesheets/main.css
Normal file
58
public/stylesheets/main.css
Normal file
@ -0,0 +1,58 @@
|
||||
#main::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
#main {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, calc(90% / 3));
|
||||
grid-template-rows: repeat(2, calc(90% / 2));
|
||||
grid-gap: 10% 5%;
|
||||
}
|
||||
|
||||
|
||||
#main .drink {
|
||||
grid-row: span 1;
|
||||
grid-column: span 1;
|
||||
background-color: rgba(57, 57, 57, 0.6);
|
||||
width: 90%;
|
||||
height: 97%;
|
||||
display: grid;
|
||||
grid-template-columns: 100%;
|
||||
grid-template-rows: repeat(3, calc(100% / 3));
|
||||
grid-row-gap: 4%;
|
||||
text-align: center;
|
||||
border-radius: 30px 10px 30px;
|
||||
color: black;
|
||||
/*box-shadow: 3px 3px 3px;*/
|
||||
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.3), 0 6px 20px 0 rgba(0, 0, 0, 0.29);
|
||||
transition: 0.4s;
|
||||
overflow: hidden;
|
||||
padding-bottom: 1%;
|
||||
}
|
||||
|
||||
|
||||
#main .drink:hover {
|
||||
background-color: rgba(57, 57, 57, 0.8);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
|
||||
.drink .thumbnail {
|
||||
grid-column: span 1;
|
||||
grid-row: span 2;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-height: 100%;
|
||||
overflow: hidden;
|
||||
transition: 0.5s;
|
||||
}
|
||||
|
||||
|
||||
.drink .drinkName {
|
||||
font-family: Ubuntu, sans-serif;
|
||||
grid-column: span 1;
|
||||
grid-row: span 1;
|
||||
font-size: 150%;
|
||||
}
|
@ -3,7 +3,8 @@
|
||||
display: none; /* Hidden by default */
|
||||
position: fixed; /* Stay in place */
|
||||
z-index: 1; /* Sit on top */
|
||||
padding-top: 15%; /* Location of the box */
|
||||
padding-top: 8%; /* Location of the box */
|
||||
padding-bottom: 1%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%; /* Full width */
|
||||
@ -24,6 +25,7 @@
|
||||
width: 80%;
|
||||
text-align: center;
|
||||
background-color: #FEFEFE;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
|
||||
|
104
public/stylesheets/setup.css
Normal file
104
public/stylesheets/setup.css
Normal file
@ -0,0 +1,104 @@
|
||||
#setup {
|
||||
padding-left: 5%;
|
||||
padding-right: 5%;
|
||||
padding-top: 1%;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, calc(95% / 2));
|
||||
grid-template-rows: repeat(7, calc(100% / 7));
|
||||
grid-gap: 2% 5%;
|
||||
}
|
||||
|
||||
#setup_slots {
|
||||
min-width: 55px;
|
||||
width: 10%;
|
||||
margin-left: 3%;
|
||||
border: 1px solid aliceblue;
|
||||
}
|
||||
|
||||
#setup #setupContainersDiv {
|
||||
grid-row: span 5;
|
||||
grid-column: span 2;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
border: 1px solid cadetblue;
|
||||
border-radius: 5px;
|
||||
padding: 1% 10px;
|
||||
overflow: auto;
|
||||
|
||||
}
|
||||
|
||||
#setup #setupContainersDiv #containerAddBtn {
|
||||
position: relative;
|
||||
left: 43%;
|
||||
bottom: 10%;
|
||||
}
|
||||
|
||||
#setup #setupLEDDiv {
|
||||
grid-row: span 3;
|
||||
grid-column: span 1;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
border: 1px solid cadetblue;
|
||||
border-radius: 5px;
|
||||
padding: 1% 10px;
|
||||
margin-right: 10%;
|
||||
|
||||
}
|
||||
|
||||
#setup #setupExtraDiv {
|
||||
grid-row: span 3;
|
||||
grid-column: span 1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
border: 1px solid cadetblue;
|
||||
border-radius: 5px;
|
||||
padding: 1% 10px;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
#setupContainers .setupContainer {
|
||||
color: black;
|
||||
text-align: left;
|
||||
width: calc(100% / 2.3);
|
||||
height: 12%;
|
||||
display: inline-block;
|
||||
margin-bottom: 3%;
|
||||
border: 1px solid gray;
|
||||
margin-left: 2%;
|
||||
margin-right: 2%;
|
||||
border-radius: 10px;
|
||||
padding: 3% 10px;
|
||||
transition: 0.5s;
|
||||
background-color: rgb(111, 109, 109);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.removeSlowly {
|
||||
animation: removeSlowlyWidth 0.8s forwards;
|
||||
}
|
||||
@keyframes removeSlowlyWidth {
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.error {
|
||||
background-color: rgba(255,0,0,0.5) !important;
|
||||
}
|
||||
|
||||
#setupContainers .setupContainer p {
|
||||
margin-bottom: 3%;
|
||||
font-weight: 600;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
|
||||
#setupContainers .setupContainer select {
|
||||
margin-bottom: 2%;
|
||||
|
||||
}
|
@ -1,15 +1,21 @@
|
||||
@import url("/stylesheets/colors.css");
|
||||
@import url("/stylesheets/fonts.css");
|
||||
@import url("/stylesheets/buttons.css");
|
||||
@import url("/stylesheets/inputs.css");
|
||||
@import url("/stylesheets/modal.css");
|
||||
|
||||
@import url("/stylesheets/main.css");
|
||||
@import url("/stylesheets/setup.css");
|
||||
|
||||
:root {
|
||||
cursor: none !important;
|
||||
/*cursor: none !important;*/
|
||||
}
|
||||
|
||||
|
||||
html * {cursor: none !important}
|
||||
html * {
|
||||
/*cursor: none !important*/
|
||||
-webkit-user-select: none; /* Safari */
|
||||
-ms-user-select: none; /* IE 10 and IE 11 */
|
||||
user-select: none; /* Standard syntax */
|
||||
}
|
||||
|
||||
|
||||
body::-webkit-scrollbar {
|
||||
@ -21,8 +27,17 @@ body {
|
||||
color: white;
|
||||
scroll-behavior: smooth;
|
||||
font-family: "Roboto", serif;
|
||||
font-style: normal;
|
||||
cursor: none !important;
|
||||
/*cursor: none !important;*/
|
||||
-webkit-user-select: none; /* Safari */
|
||||
-ms-user-select: none; /* IE 10 and IE 11 */
|
||||
user-select: none; /* Standard syntax */
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 1.7em;
|
||||
font-weight: 500;
|
||||
margin-bottom: 2%;
|
||||
color: black;
|
||||
}
|
||||
|
||||
|
||||
@ -92,6 +107,15 @@ body {
|
||||
border: 0;
|
||||
border-radius: 0 8px 0 0;
|
||||
color: white;
|
||||
float: left;
|
||||
}
|
||||
|
||||
|
||||
#overlay #bottom #containers {
|
||||
height: 100%;
|
||||
width: 30%;
|
||||
float: right;
|
||||
/* todo */
|
||||
}
|
||||
|
||||
|
||||
@ -103,14 +127,20 @@ body {
|
||||
height: 80%;
|
||||
}
|
||||
|
||||
|
||||
#menu {
|
||||
display: none;
|
||||
background-color: blue;
|
||||
width: 100%;
|
||||
.pane {
|
||||
height: 100%;
|
||||
padding: 1% 2%;
|
||||
overflow: auto;
|
||||
ms-overflow-style: none; /* IE and Edge */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
scroll-behavior: smooth;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.hiddenPane {
|
||||
display: none !important;
|
||||
transition: 0.2s;
|
||||
}
|
||||
|
||||
#settings {
|
||||
display: none;
|
||||
@ -119,67 +149,3 @@ body {
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
|
||||
#main::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
#main {
|
||||
display: grid;
|
||||
height: 100%;
|
||||
padding: 1% 2%;
|
||||
grid-template-columns: repeat(3, calc(90% / 3));
|
||||
grid-template-rows: repeat(2, calc(90% / 2));
|
||||
grid-gap: 10% 5%;
|
||||
overflow: auto;
|
||||
ms-overflow-style: none; /* IE and Edge */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
|
||||
#main .drink {
|
||||
grid-row: span 1;
|
||||
grid-column: span 1;
|
||||
background-color: rgba(57, 57, 57, 0.6);
|
||||
width: 90%;
|
||||
height: 97%;
|
||||
display: grid;
|
||||
grid-template-columns: 100%;
|
||||
grid-template-rows: repeat(3, calc(100% / 3));
|
||||
grid-row-gap: 4%;
|
||||
text-align: center;
|
||||
border-radius: 30px 10px 30px;
|
||||
color: black;
|
||||
/*box-shadow: 3px 3px 3px;*/
|
||||
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.3), 0 6px 20px 0 rgba(0, 0, 0, 0.29);
|
||||
transition: 0.4s;
|
||||
overflow: hidden;
|
||||
padding-bottom: 1%;
|
||||
}
|
||||
|
||||
#main .drink:hover {
|
||||
background-color: rgba(57, 57, 57, 0.8);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
|
||||
.drink .thumbnail {
|
||||
grid-column: span 1;
|
||||
grid-row: span 2;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-height: 100%;
|
||||
overflow: hidden;
|
||||
transition: 0.5s;
|
||||
}
|
||||
|
||||
|
||||
.drink .drinkName {
|
||||
font-family: Ubuntu, sans-serif;
|
||||
grid-column: span 1;
|
||||
grid-row: span 1;
|
||||
font-size: 150%;
|
||||
}
|
3
src/HX711.ts
Normal file
3
src/HX711.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export class HX711 {
|
||||
|
||||
}
|
20
src/LEDHandler.ts
Normal file
20
src/LEDHandler.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import ws281x from "rpi-ws281x-native";
|
||||
|
||||
export class LEDHandler {
|
||||
private static channel = ws281x(10, {stripType: 'ws2812'});
|
||||
private static currentInterval: NodeJS.Timer;
|
||||
|
||||
public static waterfall() {
|
||||
clearInterval(this.currentInterval);
|
||||
this.currentInterval = setInterval(() => {
|
||||
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
public static pulseGreen() {
|
||||
clearInterval(this.currentInterval);
|
||||
this.currentInterval = setInterval(() => {
|
||||
|
||||
}, 1000);
|
||||
}
|
||||
}
|
38
src/Settings.ts
Normal file
38
src/Settings.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import * as fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
export class Settings {
|
||||
static get setupDone(): boolean {
|
||||
return this._setupDone;
|
||||
}
|
||||
|
||||
private static _setupDone: boolean;
|
||||
private static json: {};
|
||||
|
||||
|
||||
public static loadSettings() {
|
||||
if (!fs.existsSync(path.join(__dirname, "/config.json"))) {
|
||||
this._setupDone = false;
|
||||
fs.writeFileSync(path.join(__dirname, "/config.json"), `{"setupDone":false}`);
|
||||
}
|
||||
|
||||
let file = fs.readFileSync(path.join(__dirname, "/config.json"));
|
||||
this.json = JSON.parse(file.toString("utf8"));
|
||||
|
||||
this._setupDone = this.json["setupDone"];
|
||||
this._setupDone = false;
|
||||
|
||||
}
|
||||
|
||||
public static saveSettings() {
|
||||
fs.writeFileSync(path.join(__dirname, "/config.json"), JSON.stringify(this.json));
|
||||
}
|
||||
|
||||
public static get(key: string): any {
|
||||
return this.json[key];
|
||||
}
|
||||
|
||||
public static set(key: string, value: any) {
|
||||
this.json[key] = value;
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
import * as dns from "dns";
|
||||
import * as ping from "net-ping";
|
||||
|
||||
export class Utils {
|
||||
public static checkInternet(): Promise<boolean> {
|
||||
|
@ -3,14 +3,18 @@ import mongoose from "mongoose";
|
||||
import {IContainer} from "./IContainer";
|
||||
|
||||
export const ContainerSchema = new Mongoose.Schema<IContainer>({
|
||||
slot: {type: Number},
|
||||
slot: {type: Number, required: true},
|
||||
volume: {type: Number, required: true, default: 1000},
|
||||
sensorEcho: Number,
|
||||
sensorTrigger: Number,
|
||||
content: {type: mongoose.Types.ObjectId},
|
||||
sensorType: String,
|
||||
sensorPin1: Number,
|
||||
sensorPin2: Number,
|
||||
pumpPin: {type: Number, required: true},
|
||||
content: {type: mongoose.Types.ObjectId, ref: "Ingredient"},
|
||||
sensorFilledMax: Number,
|
||||
sensorFilledMin: Number,
|
||||
filled: Number,
|
||||
enabled: {type: Boolean, default: false},
|
||||
autoDisabled: {type: Boolean, default: false}
|
||||
});
|
||||
|
||||
const Container = mongoose.model<IContainer>('Container', ContainerSchema);
|
||||
|
@ -1,13 +1,17 @@
|
||||
import {IIngredient} from "./IIngredient";
|
||||
import * as mongoose from "mongoose";
|
||||
|
||||
export interface IContainer extends mongoose.Document{
|
||||
export interface IContainer extends mongoose.Document {
|
||||
slot: number;
|
||||
content: IIngredient|undefined;
|
||||
content: IIngredient | undefined;
|
||||
volume: number;
|
||||
sensorFilledMin : number;
|
||||
sensorFilledMin: number;
|
||||
sensorFilledMax: number;
|
||||
sensorTrigger: number;
|
||||
sensorEcho: number;
|
||||
sensorType: string;
|
||||
sensorPin1: number;
|
||||
sensorPin2: number;
|
||||
pumpPin: number;
|
||||
filled: Number;
|
||||
enabled: boolean;
|
||||
autoDisabled: boolean;
|
||||
}
|
@ -7,11 +7,15 @@ import {IDrink} from "./database/IDrink";
|
||||
import debug from "debug";
|
||||
import {WebSocketHandler} from "./WebSocketHandler";
|
||||
import {IJob} from "./database/IJob";
|
||||
import {Utils} from "./Utils";
|
||||
import {WebSocketPayload} from "./WebSocketPayload";
|
||||
import {WebSocketEvent} from "./WebSocketEvent";
|
||||
import {HX711} from "./HX711";
|
||||
|
||||
const log = debug("itender:station");
|
||||
|
||||
export class iTender {
|
||||
static get containers(): { container: IContainer; sensor: HCSR04; pump: null }[] {
|
||||
static get containers(): { container: IContainer, sensor: HCSR04 | HX711, pump: null }[] {
|
||||
return this._containers;
|
||||
}
|
||||
|
||||
@ -25,8 +29,13 @@ export class iTender {
|
||||
|
||||
private static _status: iTenderStatus = iTenderStatus.STARTING;
|
||||
private static _currentJob: IJob | null = null;
|
||||
private static _internetConnection: boolean = false;
|
||||
|
||||
private static _containers: { container: IContainer, sensor: HCSR04, pump: null }[] = [];
|
||||
static get internetConnection(): boolean {
|
||||
return this._internetConnection;
|
||||
}
|
||||
|
||||
private static _containers: { container: IContainer, sensor: HCSR04 | HX711, pump: null }[] = [];
|
||||
private static _drinks: IDrink[];
|
||||
|
||||
static setStatus(status: iTenderStatus) {
|
||||
@ -57,8 +66,11 @@ export class iTender {
|
||||
return new Promise(async resolve => {
|
||||
for (let c of this._containers) {
|
||||
try {
|
||||
let dist = c.sensor.distance();
|
||||
c.container.filled = dist * 100 / (c.container.sensorFilledMax + c.container.sensorFilledMin);
|
||||
if (!(c.sensor instanceof HX711)) {
|
||||
let dist = c.sensor.distance();
|
||||
c.container.filled = dist * 100 / (c.container.sensorFilledMax + c.container.sensorFilledMin);
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
c.container.filled = -1;
|
||||
}
|
||||
@ -66,6 +78,14 @@ export class iTender {
|
||||
}
|
||||
log("Containers measured!");
|
||||
resolve();
|
||||
|
||||
let cons: IContainer[] = [];
|
||||
for (let c of this._containers) {
|
||||
cons.push(c.container);
|
||||
}
|
||||
|
||||
let payload = new WebSocketPayload(WebSocketEvent.CONTAINERS, false, cons);
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@ -109,7 +129,7 @@ export class iTender {
|
||||
if (c2.container._id == c._id) {
|
||||
let sensor;
|
||||
try {
|
||||
sensor = new HCSR04(c.sensorTrigger, c.sensorEcho);
|
||||
//sensor = new HCSR04(c.sensorTrigger, c.sensorEcho);
|
||||
} catch (e) {
|
||||
}
|
||||
this._containers[i] = {
|
||||
@ -125,7 +145,7 @@ export class iTender {
|
||||
if (!found) {
|
||||
let sensor;
|
||||
try {
|
||||
sensor = new HCSR04(c.sensorTrigger, c.sensorEcho);
|
||||
//sensor = new HCSR04(c.sensorTrigger, c.sensorEcho);
|
||||
} catch (e) {
|
||||
}
|
||||
this._containers.push({
|
||||
@ -160,5 +180,20 @@ export class iTender {
|
||||
}, 30000);
|
||||
}
|
||||
|
||||
public static async checkNetwork() {
|
||||
this._internetConnection = await Utils.checkInternet();
|
||||
}
|
||||
|
||||
|
||||
static refreshFromServer(): Promise<void> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
let before = iTender._status;
|
||||
iTender.setStatus(iTenderStatus.DOWNLOADING)
|
||||
// todo
|
||||
|
||||
resolve();
|
||||
iTender.setStatus(before);
|
||||
|
||||
});
|
||||
}
|
||||
}
|
@ -5,10 +5,14 @@ export enum iTenderStatus {
|
||||
READY = "READY",
|
||||
// Machine is filling your drink and destroying your leberwurst
|
||||
FILLING = "FILLING",
|
||||
// Drinks will be refreshed from global database (the internet neuland :O)
|
||||
// Drinks will be refreshed
|
||||
REFRESHING = "REFRESHING",
|
||||
// Drinks will be calculated (check containers and which drinks can be done)
|
||||
CALCULATING = "CALCULATING",
|
||||
// Download drinks from the world wide web
|
||||
DOWNLOADING = "DOWNLOADING",
|
||||
// Device is in setup mode for first setup
|
||||
SETUP = "SETUP",
|
||||
// An error happened; Oh no :(
|
||||
ERROR = "ERROR"
|
||||
}
|
54
src/main.ts
54
src/main.ts
@ -6,9 +6,8 @@ import Ingredient from "./database/Ingredient";
|
||||
import {iTender} from "./iTender";
|
||||
import {iTenderStatus} from "./iTenderStatus";
|
||||
import {Utils} from "./Utils";
|
||||
import {Category} from "./Category";
|
||||
import Drink from "./database/Drink";
|
||||
import Container from "./database/Container";
|
||||
import {Settings} from "./Settings";
|
||||
|
||||
const log = debug("itender:server");
|
||||
|
||||
@ -23,17 +22,36 @@ const wsApp = new WebsocketApp();
|
||||
//await test();
|
||||
await app.listen();
|
||||
await wsApp.listen();
|
||||
Settings.loadSettings();
|
||||
|
||||
iTender.setStatus(iTenderStatus.STARTING);
|
||||
await Utils.sleep(5000);
|
||||
await Utils.sleep(2000);
|
||||
|
||||
await init();
|
||||
if (!Settings.setupDone) {
|
||||
iTender.setStatus(iTenderStatus.SETUP);
|
||||
log("iTender is not set up yet!");
|
||||
}
|
||||
|
||||
setInterval(refresh, 1000 * 60 * 10);
|
||||
|
||||
iTender.setStatus(iTenderStatus.READY);
|
||||
function checkStart() {
|
||||
setTimeout(async () => {
|
||||
if (!Settings.setupDone) {
|
||||
checkStart();
|
||||
return;
|
||||
}
|
||||
Settings.saveSettings();
|
||||
|
||||
|
||||
await init();
|
||||
setInterval(refresh, 1000 * 60 * 10);
|
||||
iTender.setStatus(iTenderStatus.READY);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
checkStart();
|
||||
|
||||
} catch (e) {
|
||||
console.error("---- ERROR ----")
|
||||
console.error("---- ERROR ----");
|
||||
console.error(e);
|
||||
process.exit(-1);
|
||||
}
|
||||
@ -42,8 +60,19 @@ const wsApp = new WebsocketApp();
|
||||
function init(): Promise<void> {
|
||||
log("Initializing...");
|
||||
return new Promise(async resolve => {
|
||||
iTender.setStatus(iTenderStatus.STARTING);
|
||||
|
||||
// Network
|
||||
await iTender.checkNetwork();
|
||||
if (iTender.internetConnection) {
|
||||
await iTender.refreshFromServer();
|
||||
}
|
||||
|
||||
// Containers
|
||||
await iTender.refreshContainers();
|
||||
// Drinks
|
||||
await iTender.refreshDrinks();
|
||||
// Start auto checkup for stuck jobs
|
||||
await iTender.autoCheckup();
|
||||
|
||||
resolve();
|
||||
@ -52,6 +81,11 @@ function init(): Promise<void> {
|
||||
|
||||
function refresh(): Promise<void> {
|
||||
return new Promise(async resolve => {
|
||||
// Network
|
||||
await iTender.checkNetwork();
|
||||
|
||||
// Below are refreshments of containers / drinks
|
||||
|
||||
// If there is a current job, DO NOT REFRESH!
|
||||
if (iTender.currentJob)
|
||||
return;
|
||||
@ -108,16 +142,16 @@ async function test() {
|
||||
|
||||
console.log(drink);*/
|
||||
|
||||
let container = new Container();
|
||||
/*let container = new Container();
|
||||
container.slot = 2;
|
||||
container.volume = 750;
|
||||
container.sensorEcho = 28;
|
||||
container.sensorTrigger = 29;
|
||||
container.content = ingredient;
|
||||
container.sensorFilledMax = 2;
|
||||
container.sensorFilledMin = 15;
|
||||
container.sensorFilledMin = 15;*/
|
||||
|
||||
await container.save();
|
||||
//await container.save();
|
||||
|
||||
/* let container = await Container.findOne({slot: 1});
|
||||
if (!container) return;
|
||||
|
120
src/web/Modal.ts
120
src/web/Modal.ts
@ -5,21 +5,25 @@ export class Modal {
|
||||
private static currentModalId: string | undefined = "";
|
||||
|
||||
private _title: string = "iTender";
|
||||
private _content: string | undefined = "";
|
||||
private _id: string = "";
|
||||
private _loader: boolean = false;
|
||||
private _buttons: { type: string, content: string, onclick: Function }[] = [];
|
||||
private _leftCentered: boolean = false;
|
||||
private _elements: HTMLElement[] = [];
|
||||
|
||||
set leftCentered(value: boolean) {
|
||||
this._leftCentered = value;
|
||||
}
|
||||
|
||||
private static modalInClose = false;
|
||||
|
||||
constructor(id, title: string, content?: string) {
|
||||
constructor(id, title: string) {
|
||||
this._id = id;
|
||||
this._title = title;
|
||||
this._content = content;
|
||||
|
||||
let t = document.createElement("h1");
|
||||
t.innerText = title;
|
||||
this._elements.push(t);
|
||||
}
|
||||
|
||||
public static isModalOpen(): boolean {
|
||||
@ -27,13 +31,10 @@ export class Modal {
|
||||
}
|
||||
|
||||
|
||||
set title(value: string) {
|
||||
this._title = value;
|
||||
public addContent(element: HTMLElement) {
|
||||
this._elements.push(element);
|
||||
}
|
||||
|
||||
set content(value: string | undefined) {
|
||||
this._content = value;
|
||||
}
|
||||
|
||||
set id(value: string) {
|
||||
this._id = value;
|
||||
@ -43,74 +44,79 @@ export class Modal {
|
||||
this._loader = value;
|
||||
}
|
||||
|
||||
public addButton(type: ButtonType, content: string, onclick: Function) {
|
||||
this._buttons.push({type: type, content: content, onclick: onclick});
|
||||
public addButton(type: ButtonType, content: string, onclick: Function): HTMLButtonElement {
|
||||
let btn = document.createElement("button");
|
||||
btn.classList.add("btn", "btn-" + type);
|
||||
btn.onclick = () => onclick(btn);
|
||||
btn.innerText = content;
|
||||
btn.value = content;
|
||||
|
||||
this._elements.push(btn);
|
||||
return btn;
|
||||
}
|
||||
|
||||
public open() {
|
||||
if (!this._content)
|
||||
this._content = "";
|
||||
public open(): Promise<void> {
|
||||
return new Promise(async (resolve) => {
|
||||
/* if (this._leftCentered) {
|
||||
this._content = "<div style='text-align: left; padding-left: 2%;'>" + this._content;
|
||||
}*/ //todo
|
||||
|
||||
if( this._leftCentered )
|
||||
{
|
||||
this._content = "<div style='text-align: left; padding-left: 2%;'>" + this._content;
|
||||
}
|
||||
/* if (this._loader)
|
||||
this._content += "<br><div class=\"lds-ellipsis\">\n" +
|
||||
" <div></div><div></div><div></div><div></div>\n" +
|
||||
"</div>";*/ // todo
|
||||
|
||||
if (this._loader)
|
||||
this._content += "<br><div class=\"lds-ellipsis\">\n" +
|
||||
" <div></div><div></div><div></div><div></div>\n" +
|
||||
"</div>";
|
||||
/*if (this._leftCentered) {
|
||||
this._content += "</div>";
|
||||
}*/
|
||||
|
||||
for (let btn of this._buttons) {
|
||||
this._content += `<button class="btn btn-${btn.type}" onclick="(${btn.onclick})();">${btn.content}</button>`;
|
||||
}
|
||||
let elements = this._elements;
|
||||
let id = this._id;
|
||||
|
||||
if( this._leftCentered )
|
||||
{
|
||||
this._content+= "</div>";
|
||||
}
|
||||
|
||||
let title = this._title;
|
||||
let content = this._content;
|
||||
let id = this._id;
|
||||
function tryOpen()
|
||||
{
|
||||
if( Modal.modalInClose )
|
||||
{
|
||||
setTimeout( tryOpen, 50 );
|
||||
return;
|
||||
function tryOpen() {
|
||||
if (Modal.modalInClose) {
|
||||
setTimeout(tryOpen, 50);
|
||||
return;
|
||||
}
|
||||
resolve();
|
||||
Modal.open(elements, id);
|
||||
}
|
||||
Modal.open(title, content, id );
|
||||
}
|
||||
|
||||
tryOpen();
|
||||
tryOpen();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param title
|
||||
* @param content
|
||||
* @param elements
|
||||
* @param id
|
||||
*/
|
||||
public static open(title: string, content: string, id?: string): void {
|
||||
private static open(elements: HTMLElement[], id?: string): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const modal = document.getElementById("modal");
|
||||
const modalContent = document.getElementById("modalInnerContent");
|
||||
|
||||
const modal = document.getElementById("modal");
|
||||
const modalContent = document.getElementById("modalInnerContent");
|
||||
if (!modal || !modalContent) {
|
||||
reject();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!modal || !modalContent)
|
||||
return;
|
||||
modalContent.classList.add("modalBlendIn");
|
||||
modal.classList.add("modalBlendIn");
|
||||
|
||||
modalContent.classList.add("modalBlendIn");
|
||||
modal.classList.add("modalBlendIn");
|
||||
setTimeout(() => {
|
||||
modalContent.classList.remove("modalBlendIn");
|
||||
modal.classList.remove("modalBlendIn");
|
||||
resolve();
|
||||
}, 800);
|
||||
|
||||
setTimeout(() => {
|
||||
modalContent.classList.remove("modalBlendIn");
|
||||
modal.classList.remove("modalBlendIn");
|
||||
}, 800);
|
||||
modalContent.innerHTML = "";
|
||||
elements.forEach((val) => modalContent.append(val));
|
||||
//modalContent.innerHTML = `<h1 id="modalTitle">${title}</h1>${content}`;
|
||||
modal.style.display = "block";
|
||||
|
||||
modalContent.innerHTML = `<h1 id="modalTitle">${title}</h1>${content}`;
|
||||
modal.style.display = "block";
|
||||
this.currentModalId = id ? id : "null";
|
||||
});
|
||||
|
||||
this.currentModalId = id ? id : "null";
|
||||
}
|
||||
|
||||
public static close(id?: string): void {
|
||||
|
6
src/web/Pane.ts
Normal file
6
src/web/Pane.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export enum Pane {
|
||||
MAIN= "Main",
|
||||
MENU = "Menü",
|
||||
SETTINGS = "Einstellungen",
|
||||
SETUP = "Setup"
|
||||
}
|
3
src/web/Settings.ts
Normal file
3
src/web/Settings.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export class Settings {
|
||||
|
||||
}
|
7
src/web/Setup.ts
Normal file
7
src/web/Setup.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export class Setup {
|
||||
public static onSetupUpdate()
|
||||
{
|
||||
// Setup containers updated
|
||||
|
||||
}
|
||||
}
|
@ -1,11 +1,15 @@
|
||||
import {WebSocketPayload} from "../WebSocketPayload";
|
||||
import {IDrink} from "../database/IDrink";
|
||||
import {Modal} from "./Modal";
|
||||
import {ButtonType} from "./ButtonType";
|
||||
import {Pane} from "./Pane";
|
||||
import {setup} from "rpi-gpio";
|
||||
|
||||
export class WebHandler {
|
||||
private static containers = [];
|
||||
|
||||
public static onDrinkUpdate(payload: WebSocketPayload) {
|
||||
if (!payload.data) return;
|
||||
|
||||
let drinks: IDrink[] = payload.data;
|
||||
|
||||
const main = document.getElementById("main");
|
||||
@ -13,6 +17,8 @@ export class WebHandler {
|
||||
|
||||
main.style.gridTemplateRows = `repeat(${Math.round(drinks.length / 3)}, calc(90%/2))`;
|
||||
|
||||
main.innerHTML = "";
|
||||
|
||||
for (let drink of drinks) {
|
||||
let drinkEle = document.createElement("div");
|
||||
drinkEle.classList.add("drink");
|
||||
@ -30,25 +36,560 @@ export class WebHandler {
|
||||
drinkImg.src = "/images/" + drink.name + ".png";
|
||||
drinkName.innerText = drink.name;
|
||||
|
||||
/*
|
||||
let ingredients = "<ul style='list-style-type: disc;'>";
|
||||
for( let i of drink.ingredients )
|
||||
{
|
||||
for (let i of drink.ingredients) {
|
||||
ingredients += "<li>" + i.amount + "ml " + i.type.name + "</li>";
|
||||
}
|
||||
ingredients+="</ul>"
|
||||
ingredients += "</ul>"
|
||||
|
||||
drinkEle.onclick = () => {
|
||||
let modal = new Modal("drink", drink.name );
|
||||
modal.content = `<strong>Zutaten</strong><br>
|
||||
${ingredients}`
|
||||
modal.leftCentered = true;
|
||||
modal.open();
|
||||
};
|
||||
let modal = new Modal("drink", drink.name);
|
||||
|
||||
main.append(drinkEle);
|
||||
modal.content = `<strong>Zutaten</strong><br>
|
||||
${ingredients}`*/ //todo
|
||||
//modal.leftCentered = true;
|
||||
// modal.open();
|
||||
//};
|
||||
|
||||
//main.append(drinkEle);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static onContainerUpdate(payload: WebSocketPayload) {
|
||||
|
||||
}
|
||||
|
||||
static async openSetup() {
|
||||
// new
|
||||
this.openPane(Pane.SETUP);
|
||||
let menuBtn = document.getElementById("menuBtn") as HTMLButtonElement;
|
||||
let setupContainers = document.getElementById("setupContainers") as HTMLDivElement;
|
||||
menuBtn.disabled = true;
|
||||
|
||||
const containerAddBtn = document.getElementById("containerAddBtn") as HTMLButtonElement;
|
||||
containerAddBtn.onclick = () => {
|
||||
|
||||
|
||||
let con = document.createElement("div");
|
||||
|
||||
let containerName = document.createElement("p");
|
||||
containerName.innerText = "Behälter " + (setupContainers.getElementsByTagName("div").length + 1);
|
||||
con.classList.add("setupContainer");
|
||||
con.append(containerName);
|
||||
|
||||
let sensorTypeLabel = document.createElement("label");
|
||||
sensorTypeLabel.innerText = "Art des Sensors ";
|
||||
con.append(sensorTypeLabel);
|
||||
|
||||
let sensorType = document.createElement("select");
|
||||
sensorType.classList.add("input");
|
||||
let sensorTypeNone = document.createElement("option") as HTMLOptionElement;
|
||||
sensorTypeNone.innerText = "Keiner";
|
||||
sensorTypeNone.value = "0";
|
||||
sensorType.append(sensorTypeNone);
|
||||
let sensorTypeUltrasound = document.createElement("option") as HTMLOptionElement;
|
||||
sensorTypeUltrasound.innerText = "Ultraschall";
|
||||
sensorTypeUltrasound.value = "1";
|
||||
sensorType.append(sensorTypeUltrasound);
|
||||
let sensorTypeScale = document.createElement("option") as HTMLOptionElement;
|
||||
sensorTypeScale.innerText = "Wägezelle";
|
||||
sensorTypeScale.value = "2";
|
||||
sensorType.append(sensorTypeScale);
|
||||
con.append(sensorType);
|
||||
|
||||
con.append(document.createElement("br"));
|
||||
|
||||
|
||||
let selectPin = document.createElement("select");
|
||||
selectPin.style.display = "none";
|
||||
selectPin.classList.add("input");
|
||||
selectPin.style.display = "inline";
|
||||
|
||||
let noSel = document.createElement("option") as HTMLOptionElement;
|
||||
noSel.innerText = "Bitte wählen";
|
||||
noSel.value = "-1";
|
||||
noSel.disabled = true;
|
||||
|
||||
selectPin.append(noSel.cloneNode(true));
|
||||
selectPin.selectedIndex = 0;
|
||||
|
||||
const pins = [3, 5, 7, 8, 10, 11, 12, 13, 14, 15, 16, 18, 19, 21, 22, 23, 24, 26, 29, 31, 32, 33, 34, 35, 36, 37, 38, 40];
|
||||
for (let pin of pins) {
|
||||
let pinEle = document.createElement("option") as HTMLOptionElement;
|
||||
pinEle.innerText = "" + pin;
|
||||
pinEle.value = "" + pin;
|
||||
selectPin.append(pinEle);
|
||||
}
|
||||
|
||||
let pumpLabel = document.createElement("label");
|
||||
pumpLabel.innerText = "Pumpen Pin";
|
||||
con.append(pumpLabel);
|
||||
con.append(selectPin.cloneNode(true));
|
||||
|
||||
con.append(document.createElement("br"));
|
||||
|
||||
let sensor1Label = document.createElement("label");
|
||||
sensor1Label.innerText = "Sensor 1 Pin";
|
||||
con.append(sensor1Label);
|
||||
con.append(selectPin.cloneNode(true));
|
||||
|
||||
con.append(document.createElement("br"));
|
||||
|
||||
let sensor2Label = document.createElement("label");
|
||||
sensor2Label.innerText = "Sensor 2 Pin";
|
||||
con.append(sensor2Label);
|
||||
con.append(selectPin.cloneNode(true));
|
||||
|
||||
con.append(document.createElement("br"));
|
||||
|
||||
let removeBtn = document.createElement("button");
|
||||
removeBtn.classList.add("btn", "btn-danger");
|
||||
removeBtn.onclick = () => {
|
||||
con.classList.add("removeSlowly");
|
||||
setTimeout(() => {
|
||||
con.remove();
|
||||
let i = 1;
|
||||
for (let elementsByTagNameElement of setupContainers.getElementsByTagName("div")) {
|
||||
let e = elementsByTagNameElement.getElementsByTagName("p")[0] as HTMLParagraphElement;
|
||||
e.innerText = "Behälter " + i;
|
||||
i++;
|
||||
}
|
||||
}, 750);
|
||||
|
||||
}
|
||||
removeBtn.style.float = "right";
|
||||
removeBtn.innerText = "Entfernen";
|
||||
con.append(removeBtn);
|
||||
|
||||
setupContainers.append(con);
|
||||
|
||||
};
|
||||
return;
|
||||
|
||||
// old
|
||||
Modal.close();
|
||||
let modal = new Modal("setup", "Setup 1/2");
|
||||
|
||||
let containers: { container: HTMLDivElement, slot: number, pumpPin: HTMLSelectElement, sensorType: HTMLSelectElement, sensorPin1: HTMLSelectElement, sensorPin2: HTMLSelectElement, volume: HTMLSelectElement }[] = [];
|
||||
|
||||
let onchange = () => {
|
||||
let containerEle = document.getElementById("setup_containers") as HTMLDivElement;
|
||||
let containerNumber = document.getElementById("setup_slots") as HTMLInputElement;
|
||||
if (!containerEle || !containerNumber) return;
|
||||
|
||||
let i = 1;
|
||||
let oldElements = containerEle.getElementsByTagName("div");
|
||||
for (let e of oldElements) {
|
||||
if (i > containerNumber.valueAsNumber) {
|
||||
e.remove();
|
||||
containers.pop();
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
for (let i = containerEle.getElementsByTagName("div").length; i < containerNumber.valueAsNumber; i++) {
|
||||
let con = document.createElement("div");
|
||||
let thisContainer = {};
|
||||
thisContainer["container"] = con;
|
||||
con.classList.add("setupContainer");
|
||||
|
||||
let containerName = document.createElement("p");
|
||||
containerName.innerText = "Container " + (i + 1);
|
||||
thisContainer["slot"] = i + 1;
|
||||
|
||||
con.append(containerName);
|
||||
|
||||
let sensorTypeLabel = document.createElement("label");
|
||||
sensorTypeLabel.innerText = "Art des Sensors ";
|
||||
con.append(sensorTypeLabel);
|
||||
|
||||
let sensorType = document.createElement("select");
|
||||
sensorType.classList.add("input");
|
||||
let sensorTypeNone = document.createElement("option") as HTMLOptionElement;
|
||||
sensorTypeNone.innerText = "Keiner";
|
||||
sensorTypeNone.value = "0";
|
||||
sensorType.append(sensorTypeNone);
|
||||
let sensorTypeUltrasound = document.createElement("option") as HTMLOptionElement;
|
||||
sensorTypeUltrasound.innerText = "Ultraschall";
|
||||
sensorTypeUltrasound.value = "1";
|
||||
sensorType.append(sensorTypeUltrasound);
|
||||
let sensorTypeScale = document.createElement("option") as HTMLOptionElement;
|
||||
sensorTypeScale.innerText = "Wägezelle";
|
||||
sensorTypeScale.value = "2";
|
||||
sensorType.append(sensorTypeScale);
|
||||
|
||||
|
||||
con.append(sensorType);
|
||||
thisContainer["sensorType"] = sensorType;
|
||||
|
||||
con.append(document.createElement("br"));
|
||||
|
||||
|
||||
let changeWhenTypeOfSensor: HTMLElement[] = [];
|
||||
let list = ["Pumpen Port ", "Sensor 1 ", "Sensor 2 "];
|
||||
for (let t of list) {
|
||||
let labelElement = document.createElement("label");
|
||||
labelElement.innerText = t;
|
||||
changeWhenTypeOfSensor.push(labelElement);
|
||||
con.append(labelElement);
|
||||
|
||||
let selectElement = document.createElement("select");
|
||||
labelElement.style.display = "none";
|
||||
selectElement.style.display = "none";
|
||||
selectElement.classList.add("input");
|
||||
if (t == "Sensor 1 ") {
|
||||
thisContainer["sensorPin1"] = selectElement;
|
||||
} else if (t == "Sensor 2 ") {
|
||||
thisContainer["sensorPin2"] = selectElement;
|
||||
} else {
|
||||
thisContainer["pumpPin"] = selectElement;
|
||||
labelElement.style.display = "inline";
|
||||
selectElement.style.display = "inline";
|
||||
}
|
||||
|
||||
let noSel = document.createElement("option") as HTMLOptionElement;
|
||||
noSel.innerText = "Bitte wählen";
|
||||
noSel.value = "-1";
|
||||
noSel.disabled = true;
|
||||
|
||||
selectElement.append(noSel); //loveyou
|
||||
selectElement.selectedIndex = 0;
|
||||
|
||||
const pins = [3, 5, 7, 8, 10, 11, 12, 13, 14, 15, 16, 18, 19, 21, 22, 23, 24, 26, 29, 31, 32, 33, 34, 35, 36, 37, 38, 40];
|
||||
for (let pin of pins) {
|
||||
let pinEle = document.createElement("option") as HTMLOptionElement;
|
||||
pinEle.innerText = "" + pin;
|
||||
pinEle.value = "" + pin;
|
||||
selectElement.append(pinEle);
|
||||
}
|
||||
con.append(selectElement);
|
||||
|
||||
let br = document.createElement("br");
|
||||
con.append(br);
|
||||
}
|
||||
|
||||
sensorType.onchange = () => {
|
||||
// 0 => ultrasound; 1 => scale
|
||||
if (sensorType.value == "1") {
|
||||
thisContainer["sensorPin1"].style.display = "inline";
|
||||
thisContainer["sensorPin2"].style.display = "inline";
|
||||
changeWhenTypeOfSensor[1].style.display = "inline";
|
||||
changeWhenTypeOfSensor[2].style.display = "inline";
|
||||
changeWhenTypeOfSensor[1].innerText = "Trigger Pin ";
|
||||
changeWhenTypeOfSensor[2].innerText = "Echo Pin ";
|
||||
} else if (sensorType.value == "2") {
|
||||
thisContainer["sensorPin1"].style.display = "inline";
|
||||
thisContainer["sensorPin2"].style.display = "inline";
|
||||
changeWhenTypeOfSensor[1].style.display = "inline";
|
||||
changeWhenTypeOfSensor[2].style.display = "inline";
|
||||
changeWhenTypeOfSensor[1].innerText = "Clock Pin ";
|
||||
changeWhenTypeOfSensor[2].innerText = "Data Pin ";
|
||||
} else {
|
||||
thisContainer["sensorPin1"].style.display = "none";
|
||||
thisContainer["sensorPin2"].style.display = "none";
|
||||
changeWhenTypeOfSensor[1].style.display = "none";
|
||||
changeWhenTypeOfSensor[2].style.display = "none";
|
||||
changeWhenTypeOfSensor[1].innerText = "Sensor 1 ";
|
||||
changeWhenTypeOfSensor[2].innerText = "Sensor 2 ";
|
||||
}
|
||||
};
|
||||
|
||||
let labelElement = document.createElement("label");
|
||||
labelElement.innerText = "Gesamtvolumen (ml) ";
|
||||
con.append(labelElement);
|
||||
|
||||
let selectElement = document.createElement("select");
|
||||
selectElement.classList.add("input");
|
||||
const mls = [50, 100, 200, 250, 300, 330, 500, 750, 1000, 1250, 1500, 2000, 2500, 5000, 10000];
|
||||
for (let ml of mls) {
|
||||
let pinEle = document.createElement("option") as HTMLOptionElement;
|
||||
pinEle.innerText = "" + ml;
|
||||
pinEle.value = "" + ml;
|
||||
selectElement.append(pinEle);
|
||||
thisContainer["volume"] = selectElement;
|
||||
}
|
||||
selectElement.selectedIndex = 7;
|
||||
|
||||
con.append(selectElement);
|
||||
|
||||
let br = document.createElement("br");
|
||||
con.append(br);
|
||||
|
||||
containerEle.append(con);
|
||||
let cast = thisContainer as { container: HTMLDivElement, slot: number, pumpPin: HTMLSelectElement, sensorType: HTMLSelectElement, sensorPin1: HTMLSelectElement, sensorPin2: HTMLSelectElement, volume: HTMLSelectElement };
|
||||
containers.push(cast);
|
||||
console.log(cast);
|
||||
}
|
||||
};
|
||||
|
||||
let div1 = document.createElement("div");
|
||||
div1.style.marginBottom = "2%";
|
||||
modal.addContent(div1);
|
||||
let labelElement = document.createElement("label");
|
||||
labelElement.innerText = "Anzahl Slots";
|
||||
div1.append(labelElement);
|
||||
|
||||
let numberInputElement = document.createElement("input") as HTMLInputElement;
|
||||
numberInputElement.type = "number";
|
||||
numberInputElement.id = "setup_slots";
|
||||
numberInputElement.classList.add("input");
|
||||
numberInputElement.style.width = "20%;"
|
||||
numberInputElement.value = "0";
|
||||
numberInputElement.min = "0";
|
||||
numberInputElement.max = "30";
|
||||
numberInputElement.onchange = () => {
|
||||
onchange();
|
||||
};
|
||||
div1.append(numberInputElement);
|
||||
|
||||
let divContainers = document.createElement("div");
|
||||
divContainers.id = "setup_containers";
|
||||
modal.addContent(divContainers);
|
||||
|
||||
let status = document.createElement("p");
|
||||
status.style.color = "black";
|
||||
status.innerHTML = "Drücke 'Weiter' zum Speichern";
|
||||
modal.addContent(status);
|
||||
|
||||
modal.addButton(ButtonType.SUCCESS, "Weiter", () => {
|
||||
status.innerHTML = "...";
|
||||
status.style.color = "gray";
|
||||
if (numberInputElement.valueAsNumber < 0 || numberInputElement.valueAsNumber > 30) {
|
||||
numberInputElement.value = "0";
|
||||
status.style.color = "black";
|
||||
status.innerHTML = "Automatische Änderungen überprüfen!";
|
||||
return;
|
||||
}
|
||||
|
||||
// Check
|
||||
if (numberInputElement.valueAsNumber != containers.length) {
|
||||
onchange();
|
||||
status.style.color = "black";
|
||||
status.innerHTML = "Automatische Änderungen überprüfen!";
|
||||
return;
|
||||
}
|
||||
|
||||
if (containers.length == 0) {
|
||||
status.style.color = "red";
|
||||
status.innerHTML = "Es muss mindestens ein Container hinzugefügt werden!";
|
||||
return;
|
||||
}
|
||||
|
||||
let newContainers: { slot: number, pumpPin: number, sensorPin1: number, sensorPin2: number, volume: number }[] = [];
|
||||
let ok = true;
|
||||
for (let c of containers) {
|
||||
for (let c2 of containers) {
|
||||
if (c.slot == c.slot) continue;
|
||||
|
||||
let pins = [c.sensorPin1.value, c.sensorPin2.value, c.pumpPin.value, c2.sensorPin1.value, c2.sensorPin2.value, c2.pumpPin.value];
|
||||
|
||||
let i = 0;
|
||||
for (let pin of pins) {
|
||||
let j = 0;
|
||||
if (pin == "-1") {
|
||||
console.log("Skip ", pin);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (let pin2 of pins) {
|
||||
// Wenn index derselbe ist, ignorieren
|
||||
if (j == i) {
|
||||
console.log("Index skip ", pin);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pin2 == "-1") {
|
||||
console.log("Skip ", pin);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pin == pin2) {
|
||||
console.log("Pin same error", pin, pin2, c, c2)
|
||||
ok = false;
|
||||
c.container.classList.add("error");
|
||||
c2.container.classList.add("error");
|
||||
setTimeout(() => {
|
||||
c.container.classList.remove("error");
|
||||
c2.container.classList.remove("error");
|
||||
}, 2000);
|
||||
break;
|
||||
}
|
||||
|
||||
j++;
|
||||
}
|
||||
i++;
|
||||
if (!ok) break;
|
||||
}
|
||||
if (!ok) break;
|
||||
|
||||
/*console.log(c, c2);
|
||||
console.log(c.sensorPin2.value == c2.sensorPin2.value);
|
||||
if (
|
||||
c.pumpPin.value == c2.pumpPin.value ||
|
||||
(c.sensorType.value != "0" && (
|
||||
(c.sensorPin1.value == c2.sensorPin1.value)
|
||||
|| (c.sensorPin2.value == c2.sensorPin2.value)
|
||||
|| (c.sensorPin1.value == c2.sensorPin2.value)
|
||||
|| (c.sensorPin2.value == c2.sensorPin1.value)))
|
||||
) {
|
||||
console.log("Check not ok")
|
||||
ok = false;
|
||||
c.container.classList.add("error");
|
||||
c2.container.classList.add("error");
|
||||
setTimeout(() => {
|
||||
c.container.classList.remove("error");
|
||||
c2.container.classList.remove("error");
|
||||
}, 2000);
|
||||
break;
|
||||
}*/
|
||||
}
|
||||
if (!ok) break;
|
||||
|
||||
if (c.pumpPin.value == "-1" || (c.sensorType.value != "0" && (c.sensorPin1.value == "-1" || c.sensorPin2.value == "-1"))) {
|
||||
console.log("Local check invalid", c);
|
||||
ok = false;
|
||||
c.container.classList.add("error");
|
||||
|
||||
setTimeout(() => {
|
||||
c.container.classList.remove("error");
|
||||
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
|
||||
let pins = [c.sensorPin1.value, c.sensorPin2.value, c.pumpPin.value];
|
||||
|
||||
let i = 0;
|
||||
for (let pin of pins) {
|
||||
let j = 0;
|
||||
if (pin == "-1") {
|
||||
console.log("Skip ", pin);
|
||||
continue;
|
||||
}
|
||||
for (let pin2 of pins) {
|
||||
// Wenn index derselbe ist, ignorieren
|
||||
if (j == i) {
|
||||
console.log("Index skip ", pin);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pin2 == "-1") {
|
||||
console.log("Skip ", pin);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pin == pin2) {
|
||||
console.log("Pin same error", pin, pin2, c)
|
||||
ok = false;
|
||||
c.container.classList.add("error");
|
||||
setTimeout(() => {
|
||||
c.container.classList.remove("error");
|
||||
}, 2000);
|
||||
break;
|
||||
}
|
||||
|
||||
j++;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
/*if (c.pumpPin.value == c.sensorPin1.value || c.pumpPin.value == c.sensorPin2.value || c.sensorPin1.value == c.sensorPin2.value) {
|
||||
ok = false;
|
||||
c.container.classList.add("error");
|
||||
|
||||
setTimeout(() => {
|
||||
c.container.classList.remove("error");
|
||||
|
||||
}, 1000);
|
||||
break;
|
||||
}*/
|
||||
|
||||
newContainers.push({
|
||||
slot: c.slot,
|
||||
sensorPin1: parseInt(c.sensorPin1.value),
|
||||
sensorPin2: parseInt(c.sensorPin2.value),
|
||||
pumpPin: parseInt(c.pumpPin.value),
|
||||
volume: parseInt(c.volume.value)
|
||||
});
|
||||
}
|
||||
if (!ok) {
|
||||
status.style.color = "red";
|
||||
status.innerHTML = "Problem erkannt<br>Bitte überprüfen, ob alle benötigten Felder gesetzt sind<br>und ob Pins nicht mehrfach belegt sind!"
|
||||
return;
|
||||
}
|
||||
|
||||
// todo Hier konfiguration an Server senden und speichern
|
||||
|
||||
// zurück und hinsenden mittels function die hier in der WebHandler.ts gespeichert wird maybe?
|
||||
|
||||
status.style.color = "green";
|
||||
status.innerHTML = "Konfiguration gespeichert!";
|
||||
|
||||
let tareModal = new Modal("setup", "Setup 2/2");
|
||||
|
||||
let container = document.createElement("div");
|
||||
tareModal.addContent(container);
|
||||
|
||||
let txt = document.createElement("p");
|
||||
txt.innerHTML = `Um das Setup abzuschließen, müssen die Sensoren eingestellt werden.<br>
|
||||
Bitte zunächst jegliche Behälter von/unter den Sensoren entfernen.<br>Zum fortfahren "Messen"-Schaltfläche berühren.<br>`;
|
||||
tareModal.addContent(txt);
|
||||
|
||||
|
||||
let btn = tareModal.addButton(ButtonType.SUCCESS, "Messen", () => {
|
||||
txt.innerHTML = "<strong>Messung läuft...</strong><br><span style='text-decoration: underline'>Gerät nicht berühren!</span><br><div class=\"lds-ellipsis\">\n" +
|
||||
" <div></div><div></div><div></div><div></div>\n" +
|
||||
"</div>";
|
||||
btn.disabled = true;
|
||||
btn.innerText = "Bitte warten";
|
||||
});
|
||||
// todo send messung to server to start
|
||||
|
||||
tareModal.open();
|
||||
});
|
||||
|
||||
//await modal.open();
|
||||
|
||||
let setupNumber = document.getElementById("setup_slots") as HTMLInputElement;
|
||||
setupNumber.focus();
|
||||
}
|
||||
|
||||
public static openPane(pane: Pane): void {
|
||||
let mainPanel = document.getElementById("main") as HTMLDivElement;
|
||||
let setupPanel = document.getElementById("setup") as HTMLDivElement;
|
||||
let menuPanel = document.getElementById("menu") as HTMLDivElement;
|
||||
let settingsPanel = document.getElementById("settings") as HTMLDivElement;
|
||||
|
||||
|
||||
mainPanel.classList.add("hiddenPane");
|
||||
setupPanel.classList.add("hiddenPane");
|
||||
menuPanel.classList.add("hiddenPane");
|
||||
settingsPanel.classList.add("hiddenPane");
|
||||
|
||||
|
||||
switch (pane) {
|
||||
case Pane.MAIN: {
|
||||
mainPanel.classList.remove("hiddenPane");
|
||||
break;
|
||||
}
|
||||
case Pane.MENU: {
|
||||
menuPanel.classList.remove("hiddenPane");
|
||||
break;
|
||||
}
|
||||
case Pane.SETUP: {
|
||||
setupPanel.classList.remove("hiddenPane");
|
||||
break;
|
||||
}
|
||||
case Pane.SETTINGS: {
|
||||
settingsPanel.classList.remove("hiddenPane");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let title = document.getElementById("title") as HTMLTitleElement;
|
||||
title.innerText = pane.toString();
|
||||
}
|
||||
}
|
@ -42,20 +42,29 @@ export class WebWebSocketHandler {
|
||||
case iTenderStatus.READY: {
|
||||
Modal.close("start");
|
||||
Modal.close("refreshing");
|
||||
Modal.close("setup");
|
||||
break;
|
||||
}
|
||||
case iTenderStatus.STARTING: {
|
||||
let modal = new Modal("start", "Willkommen!", `Einen Augenblick bitte<br>iTender startet...`);
|
||||
let modal = new Modal("start", "Willkommen!");
|
||||
let txt = document.createElement("p");
|
||||
txt.innerHTML = `Einen Augenblick bitte<br>iTender startet...`;
|
||||
modal.addContent(txt);
|
||||
modal.loader = true;
|
||||
modal.open();
|
||||
break;
|
||||
}
|
||||
case iTenderStatus.REFRESHING: {
|
||||
let modal = new Modal("refreshing", "Aktualisieren...", `Einen Augenblick bitte<br>iTender aktualisiert die Getränke...`);
|
||||
let modal = new Modal("refreshing", "Aktualisieren...");
|
||||
let txt = document.createElement("p");
|
||||
txt.innerHTML = `Einen Augenblick bitte<br>iTender aktualisiert die Getränke...`;
|
||||
modal.addContent(txt);
|
||||
modal.loader = true;
|
||||
modal.open();
|
||||
break;
|
||||
|
||||
}
|
||||
case iTenderStatus.SETUP: {
|
||||
WebHandler.openSetup();
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -65,18 +74,22 @@ export class WebWebSocketHandler {
|
||||
WebHandler.onDrinkUpdate(payload);
|
||||
break;
|
||||
}
|
||||
|
||||
case WebSocketEvent.CONTAINERS: {
|
||||
WebHandler.onContainerUpdate(payload);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private onOpen(event) {
|
||||
console.log("[WS] Connected", event);
|
||||
|
||||
let connectionElement = document.getElementById("right");
|
||||
if (connectionElement)
|
||||
{
|
||||
/*let connectionElement = document.getElementById("right");
|
||||
if (connectionElement) {
|
||||
connectionElement.innerText = "Verbunden";
|
||||
connectionElement.style.color = "green";
|
||||
}
|
||||
}*/
|
||||
|
||||
}
|
||||
|
||||
@ -84,14 +97,18 @@ export class WebWebSocketHandler {
|
||||
console.error("[WS] Closed!", event);
|
||||
if (event.wasClean) {
|
||||
let modal = new Modal("socketClosed", "Sitzung beendet!");
|
||||
modal.content = `Diese Sitzung wurde beendet, da der iTender nun an einem anderen Gerät bzw. an dem Hauptgerät gesteuert wird.<br><br>`;
|
||||
let txt = document.createElement("p");
|
||||
txt.innerHTML = `Diese Sitzung wurde beendet, da der iTender nun an einem anderen Gerät bzw. an dem Hauptgerät gesteuert wird.<br><br>`;
|
||||
modal.addContent(txt);
|
||||
modal.addButton(ButtonType.SUCCESS, "Sitzung wiederherstellen", () => {
|
||||
window.location.reload();
|
||||
});
|
||||
modal.open();
|
||||
} else {
|
||||
let modal = new Modal("socketClosed", "Verbindungsproblem!");
|
||||
modal.content = `Die Benutzeroberfläche hat die Verbindung mit dem Gerät verloren.<br>Die Verbindung wird wiederhergestellt...<br>`;
|
||||
let txt = document.createElement("p");
|
||||
txt.innerHTML = `Die Benutzeroberfläche hat die Verbindung mit dem Gerät verloren.<br>Die Verbindung wird wiederhergestellt...<br>`;
|
||||
modal.addContent(txt);
|
||||
modal.loader = true;
|
||||
modal.open();
|
||||
setInterval(() => {
|
||||
@ -99,19 +116,18 @@ export class WebWebSocketHandler {
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
let connectionElement = document.getElementById("right");
|
||||
if (connectionElement)
|
||||
{
|
||||
/* let connectionElement = document.getElementById("right");
|
||||
if (connectionElement) {
|
||||
connectionElement.innerText = "Getrennt";
|
||||
connectionElement.style.color = "red";
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
private onError(event) {
|
||||
console.error("[WS] Error", event);
|
||||
let connectionElement = document.getElementById("right");
|
||||
/*let connectionElement = document.getElementById("right");
|
||||
if (connectionElement)
|
||||
connectionElement.innerText = "Fehler";
|
||||
connectionElement.innerText = "Fehler";*/
|
||||
//openModal("Einen Augenblick...", `Es wurde ein kritischer Fehler festgestellt.\nBitte warten Sie, während der Prozess neu gestartet wird...` );
|
||||
//window.location.reload();
|
||||
}
|
||||
|
@ -1,14 +1,19 @@
|
||||
import {WebWebSocketHandler} from "./WebWebSocketHandler";
|
||||
import {Modal} from "./Modal";
|
||||
import {WebHandler} from "./WebHandler";
|
||||
import {Pane} from "./Pane";
|
||||
|
||||
const main = document.getElementById("main");
|
||||
const time = document.getElementById("title");
|
||||
const time = document.getElementById("right");
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
console.log("DOM Loaded");
|
||||
WebHandler.openPane(Pane.MAIN);
|
||||
|
||||
let modal = new Modal("start", "iTender");
|
||||
modal.content = "Willkommen";
|
||||
let txt = document.createElement("p");
|
||||
txt.innerText = "Willkommen"
|
||||
modal.addContent(txt);
|
||||
modal.loader = true;
|
||||
//modal.open();
|
||||
connect();
|
||||
|
3
startFrontend.sh
Executable file
3
startFrontend.sh
Executable file
@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
/usr/bin/startx /usr/bin/chromium-browser --kiosk --incognito --disable-pinch --overscroll-history-navigation=0 http://localhost:3000 &
|
@ -1,8 +1,46 @@
|
||||
extends layout
|
||||
|
||||
block main
|
||||
|
||||
block setup
|
||||
div#setupContainersDiv
|
||||
h1 Behälter
|
||||
// Setup
|
||||
button.btn.btn-primary#containerAddBtn Hinzufügen
|
||||
br
|
||||
div#setupContainers
|
||||
|
||||
|
||||
div#setupLEDDiv
|
||||
h1 LED-Einstellungen
|
||||
div.inputGroup
|
||||
label Aktivieren
|
||||
input.input(type="checkbox")
|
||||
div.inputGroup
|
||||
label GPIO-Pin
|
||||
input.input(type="number" value="22" style="width:15%" disabled="disabled")
|
||||
div.inputGroup
|
||||
label Ambiente Farbe
|
||||
input.input(type="color" value="#05445e" style="width:15%" disabled="disabled")
|
||||
|
||||
div#setupExtraDiv
|
||||
h1 Erweiterte Einstellungen
|
||||
div.inputGroup
|
||||
label Remote-Verbindungen erlauben
|
||||
input.input(type="checkbox" id="setup_remoteCheckbox")
|
||||
div.inputGroup
|
||||
label Ohne WiFi Hotspot aktivieren
|
||||
input.input(type="checkbox" id="setup_hotspotCheckbox")
|
||||
div.inputGroup
|
||||
label Ohne WiFi Hotspot aktivieren
|
||||
input.input(type="checkbox" id="setup_hotspotCheckbox")
|
||||
|
||||
|
||||
button.btn.btn-success(style="grid-row: span 1; grid-column: span 2; border-radius: 15px; font-size: 1.2em;") Speichern
|
||||
|
||||
block menu
|
||||
// Menu
|
||||
|
||||
block settings
|
||||
// Settings
|
||||
|
||||
block main
|
||||
// Main is build dynamically
|
||||
|
@ -12,23 +12,22 @@ html
|
||||
|
||||
div#overlay
|
||||
div#top
|
||||
span#left <strong>Status:</strong> <span id="status"></span>
|
||||
span#left <strong>Status:</strong> <span id="status">...</span>
|
||||
span#title iTender
|
||||
span#right Verbinden...
|
||||
div#bottom
|
||||
button#menuBtn Menü
|
||||
button.btn#menuBtn Menü
|
||||
div#containers
|
||||
|
||||
div#container
|
||||
div#menu
|
||||
div.pane#setup
|
||||
block setup
|
||||
div.pane#menu
|
||||
block menu
|
||||
div#settings
|
||||
div.pane#settings
|
||||
block settings
|
||||
div#main
|
||||
div.pane#main
|
||||
block main
|
||||
|
||||
block extra
|
||||
script(src="/web.js")
|
||||
script.
|
||||
// setTimeout( () =>
|
||||
// {
|
||||
// window.location.reload();
|
||||
// }, 120000 );
|
||||
script(src="/web.js")
|
3684
yarn-error.log
Normal file
3684
yarn-error.log
Normal file
File diff suppressed because it is too large
Load Diff
36
yarn.lock
36
yarn.lock
@ -998,6 +998,13 @@
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/rpi-ws281x-native@^1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/rpi-ws281x-native/-/rpi-ws281x-native-1.0.0.tgz#7de8a1fafcd2b03f848dad3b0f721319bc8a8341"
|
||||
integrity sha512-DbbeQgMYmgFm6gHiZVeRV0rnJzQ3eNpz4zNqPOwgMpONYyhz3+ssQExduIIs77qZ8SOn/2c0RsZjMtbTdGtuxQ==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/serve-static@*":
|
||||
version "1.15.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.0.tgz#c7930ff61afb334e121a9da780aac0d9b8f34155"
|
||||
@ -1337,7 +1344,7 @@ binary-extensions@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
|
||||
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
|
||||
|
||||
bindings@^1.5.0:
|
||||
bindings@^1.3.0, bindings@^1.5.0:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df"
|
||||
integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==
|
||||
@ -2543,6 +2550,11 @@ ms@2.1.3, ms@^2.1.1:
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
|
||||
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
|
||||
|
||||
nan@2.14.*:
|
||||
version "2.14.2"
|
||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19"
|
||||
integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==
|
||||
|
||||
nan@^2.14.0, nan@^2.14.2:
|
||||
version "2.17.0"
|
||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb"
|
||||
@ -2558,6 +2570,13 @@ neo-async@^2.6.2:
|
||||
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
|
||||
integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
|
||||
|
||||
net-ping@^1.2.3:
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/net-ping/-/net-ping-1.2.3.tgz#c4dd248f6e3d8d73db6928002106bb71ceae4a40"
|
||||
integrity sha512-ZKxj/kVPKL2RIsV9nR6I8nMT8Pi3k6ciTBKxD/6gd5lga9qcNmlyqNv+dbXqYGBvHsmG9yIpsfajr8X054x2fQ==
|
||||
dependencies:
|
||||
raw-socket "*"
|
||||
|
||||
node-releases@^2.0.6:
|
||||
version "2.0.6"
|
||||
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503"
|
||||
@ -2897,6 +2916,13 @@ raw-body@2.3.3:
|
||||
iconv-lite "0.4.23"
|
||||
unpipe "1.0.0"
|
||||
|
||||
raw-socket@*:
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/raw-socket/-/raw-socket-1.7.0.tgz#cd0bcc3da52450dcb6b9efe7a6d0890bf1aaf9db"
|
||||
integrity sha512-mXqWihgwaFNmV5le0dWk5o+03M3A2zBIkC9BNaE6R0CJN9eYot++j2FIqgNSDq6/Vmu32PPI155SiiWNV2yyFQ==
|
||||
dependencies:
|
||||
nan "2.14.*"
|
||||
|
||||
readdirp@~3.6.0:
|
||||
version "3.6.0"
|
||||
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
|
||||
@ -2982,6 +3008,14 @@ rpi-gpio@^2.1.7:
|
||||
debug "^3.1.0"
|
||||
epoll "^2.0.10"
|
||||
|
||||
rpi-ws281x-native@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/rpi-ws281x-native/-/rpi-ws281x-native-1.0.4.tgz#df5a50ecfdd165f8a10df891d5eba2cec2577fcb"
|
||||
integrity sha512-ieClupQGt7PkIIak5bpGaYmymh5nS448LwrkumfwPoju6gTNjlC/GSyBiyq9sFE3FLixzsDhDoYzLQff6y1jXw==
|
||||
dependencies:
|
||||
bindings "^1.3.0"
|
||||
nan "^2.14.2"
|
||||
|
||||
safe-buffer@5.1.2:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
||||
|
Loading…
x
Reference in New Issue
Block a user