Shelly skripta
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
CC = gcc
|
CC = gcc
|
||||||
CFLAGS = -I. -I./include -I/opt/homebrew/include -std=c99 -Wall $(shell pkg-config --cflags jansson)
|
CFLAGS = -I. -I./include -I/opt/homebrew/include -I/usr/include -Wall $(shell pkg-config --cflags jansson)
|
||||||
SOURCES = src/main.c src/database.c mongoose.c sqlite3.c
|
SOURCES = src/main.c src/database.c mongoose.c sqlite3.c
|
||||||
OBJECTS = $(SOURCES:.c=.o)
|
OBJECTS = $(SOURCES:.c=.o)
|
||||||
TARGET = api_server
|
TARGET = api_server
|
||||||
|
|||||||
BIN
apiv2/api_server
BIN
apiv2/api_server
Binary file not shown.
Binary file not shown.
@@ -4,17 +4,16 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <jansson.h>
|
#include <jansson.h>
|
||||||
|
|
||||||
static const char *s_http_port = "8001";
|
static const char *s_http_port = "127.0.0.1:8001";
|
||||||
static const char *s_root_dir = "."; // Serve current directory
|
static const char *s_root_dir = "."; // Serve current directory
|
||||||
|
|
||||||
static void handle_get(struct mg_connection *c, struct mg_http_message *hm) {
|
static void handle_get(struct mg_connection *c, struct mg_http_message *hm) {
|
||||||
char query[256] = {0};
|
char query[256] = {0};
|
||||||
char *response;
|
|
||||||
|
|
||||||
// Parse query parameter 'q'
|
// Parse query parameter 'q'
|
||||||
mg_http_get_var(&hm->query, "q", query, sizeof(query));
|
mg_http_get_var(&hm->query, "q", query, sizeof(query));
|
||||||
|
|
||||||
response = db_query(query);
|
char *response = db_query(query);
|
||||||
|
|
||||||
mg_http_reply(c, 200, "Content-Type: application/json\r\n", "%s", response);
|
mg_http_reply(c, 200, "Content-Type: application/json\r\n", "%s", response);
|
||||||
|
|
||||||
@@ -62,16 +61,16 @@ static void handle_post(struct mg_connection *c, struct mg_http_message *hm) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void handle_api_call(struct mg_connection *c, struct mg_http_message *hm) {
|
static void handle_api_call(struct mg_connection *c, struct mg_http_message *hm) {
|
||||||
if (mg_casecmp(&hm->method, "GET") == 0) {
|
if (mg_strcasecmp(hm->method, mg_str("GET")) == 0) {
|
||||||
handle_get(c, hm);
|
handle_get(c, hm);
|
||||||
} else if (mg_casecmp(&hm->method, "POST") == 0) {
|
} else if (mg_strcasecmp(hm->method, mg_str("POST")) == 0) {
|
||||||
handle_post(c, hm);
|
handle_post(c, hm);
|
||||||
} else {
|
} else {
|
||||||
mg_http_reply(c, 405, "Content-Type: text/plain\r\n", "Method Not Allowed");
|
mg_http_reply(c, 405, "Content-Type: text/plain\r\n", "Method Not Allowed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
if (ev == MG_EV_HTTP_MSG) {
|
if (ev == MG_EV_HTTP_MSG) {
|
||||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
|
|
||||||
|
|||||||
1
apiv2g/go.mod
Normal file
1
apiv2g/go.mod
Normal file
@@ -0,0 +1 @@
|
|||||||
|
module apiv2g
|
||||||
25
apiv2g/main.go
Normal file
25
apiv2g/main.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
//TIP To run your code, right-click the code and select <b>Run</b>. Alternatively, click
|
||||||
|
// the <icon src="AllIcons.Actions.Execute"/> icon in the gutter and select the <b>Run</b> menu item from here.
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
//TIP Press <shortcut actionId="ShowIntentionActions"/> when your caret is at the underlined or highlighted text
|
||||||
|
// to see how GoLand suggests fixing it.
|
||||||
|
s := "gopher"
|
||||||
|
fmt.Println("Hello and welcome, %s!", s)
|
||||||
|
|
||||||
|
for i := 1; i <= 5; i++ {
|
||||||
|
//TIP You can try debugging your code. We have set one <icon src="AllIcons.Debugger.Db_set_breakpoint"/> breakpoint
|
||||||
|
// for you, but you can always add more by pressing <shortcut actionId="ToggleLineBreakpoint"/>. To start your debugging session,
|
||||||
|
// right-click your code in the editor and select the <b>Debug</b> option.
|
||||||
|
fmt.Println("i =", 100/i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//TIP See GoLand help at <a href="https://www.jetbrains.com/help/go/">jetbrains.com/help/go/</a>.
|
||||||
|
// Also, you can try interactive lessons for GoLand by selecting 'Help | Learn IDE Features' from the main menu.
|
||||||
1220
apiv2r/Cargo.lock
generated
Normal file
1220
apiv2r/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
29
apiv2r/Cargo.toml
Normal file
29
apiv2r/Cargo.toml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
|
||||||
|
[package]
|
||||||
|
name = "zoblak"
|
||||||
|
version = "2.0.0"
|
||||||
|
authors = ["Senad Uka <senad@uka.life>"]
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
actix-web = "4.0"
|
||||||
|
actix-files = "0.6"
|
||||||
|
rusqlite = "0.29.0"
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
tokio = { version = "1", features = ["full"] }
|
||||||
|
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
quickcheck = "0.9"
|
||||||
|
speculate = "0.1"
|
||||||
|
parking_lot = { version = "0.12", features = ["nightly"] }
|
||||||
|
|
||||||
|
[dependencies.rocket_contrib]
|
||||||
|
version = "0.4"
|
||||||
|
default-features = false
|
||||||
|
features = ["json", "diesel_postgres_pool"]
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = [edition2024]
|
||||||
|
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ try:
|
|||||||
except:
|
except:
|
||||||
print("onewire thermo error:", sys.exc_info()[0])
|
print("onewire thermo error:", sys.exc_info()[0])
|
||||||
|
|
||||||
# Un-comment the line below to convert the temperature to Fahrenheit.
|
# Un-comment the line below to convert the temperature to Fahrenheiq
|
||||||
# temperature = temperature * 9/5.0 + 32
|
# temperature = temperature * 9/5.0 + 32
|
||||||
|
|
||||||
print 'Temp={0:0.1f}*C'.format(temperature)
|
print 'Temp={0:0.1f}*C'.format(temperature)
|
||||||
|
|||||||
Binary file not shown.
162
shelly/syncscript.js
Normal file
162
shelly/syncscript.js
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
const CONTROLLER_ID="550";
|
||||||
|
|
||||||
|
const senzori = [
|
||||||
|
"7c:c6:b6:74:a4:be",
|
||||||
|
"7c:c6:b6:75:a1:21"
|
||||||
|
];
|
||||||
|
|
||||||
|
const temperature = ["22.0", "22.0"];
|
||||||
|
const humidity = ["50", "50"];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let ALLTERCO_MFD_ID_STR = "0ba9";
|
||||||
|
let BTHOME_SVC_ID_STR = "fcd2";
|
||||||
|
|
||||||
|
let ALLTERCO_MFD_ID = JSON.parse("0x" + ALLTERCO_MFD_ID_STR);
|
||||||
|
let BTHOME_SVC_ID = JSON.parse("0x" + BTHOME_SVC_ID_STR);
|
||||||
|
|
||||||
|
let SCAN_DURATION = BLE.Scanner.INFINITE_SCAN;
|
||||||
|
|
||||||
|
let uint8 = 0;
|
||||||
|
let int8 = 1;
|
||||||
|
let uint16 = 2;
|
||||||
|
let int16 = 3;
|
||||||
|
let uint24 = 4;
|
||||||
|
let int24 = 5;
|
||||||
|
|
||||||
|
function getByteSize(type) {
|
||||||
|
if (type === uint8 || type === int8) return 1;
|
||||||
|
if (type === uint16 || type === int16) return 2;
|
||||||
|
if (type === uint24 || type === int24) return 3;
|
||||||
|
//impossible as advertisements are much smaller;
|
||||||
|
return 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
let BTH = [];
|
||||||
|
BTH[0x00] = { n: "pid", t: uint8 };
|
||||||
|
BTH[0x01] = { n: "Battery", t: uint8, u: "%" };
|
||||||
|
BTH[0x3a] = { n: "Button", t: uint8 };
|
||||||
|
BTH[0x2e] = { n: "Humidity", t: uint8 };
|
||||||
|
BTH[0x45] = { n: "Temperature", t: int16, f: 0.1 };
|
||||||
|
|
||||||
|
|
||||||
|
let BTHomeDecoder = {
|
||||||
|
utoi: function (num, bitsz) {
|
||||||
|
let mask = 1 << (bitsz - 1);
|
||||||
|
return num & mask ? num - (1 << bitsz) : num;
|
||||||
|
},
|
||||||
|
getUInt8: function (buffer) {
|
||||||
|
return buffer.at(0);
|
||||||
|
},
|
||||||
|
getInt8: function (buffer) {
|
||||||
|
return this.utoi(this.getUInt8(buffer), 8);
|
||||||
|
},
|
||||||
|
getUInt16LE: function (buffer) {
|
||||||
|
return 0xffff & ((buffer.at(1) << 8) | buffer.at(0));
|
||||||
|
},
|
||||||
|
getInt16LE: function (buffer) {
|
||||||
|
return this.utoi(this.getUInt16LE(buffer), 16);
|
||||||
|
},
|
||||||
|
getUInt24LE: function (buffer) {
|
||||||
|
return (
|
||||||
|
0x00ffffff & ((buffer.at(2) << 16) | (buffer.at(1) << 8) | buffer.at(0))
|
||||||
|
);
|
||||||
|
},
|
||||||
|
getInt24LE: function (buffer) {
|
||||||
|
return this.utoi(this.getUInt24LE(buffer), 24);
|
||||||
|
},
|
||||||
|
getBufValue: function (type, buffer) {
|
||||||
|
if (buffer.length < getByteSize(type)) return null;
|
||||||
|
let res = null;
|
||||||
|
if (type === uint8) res = this.getUInt8(buffer);
|
||||||
|
if (type === int8) res = this.getInt8(buffer);
|
||||||
|
if (type === uint16) res = this.getUInt16LE(buffer);
|
||||||
|
if (type === int16) res = this.getInt16LE(buffer);
|
||||||
|
if (type === uint24) res = this.getUInt24LE(buffer);
|
||||||
|
if (type === int24) res = this.getInt24LE(buffer);
|
||||||
|
return res;
|
||||||
|
},
|
||||||
|
unpack: function (buffer) {
|
||||||
|
// beacons might not provide BTH service data
|
||||||
|
if (typeof buffer !== "string" || buffer.length === 0) return null;
|
||||||
|
let result = {};
|
||||||
|
let _dib = buffer.at(0);
|
||||||
|
result["encryption"] = _dib & 0x1 ? true : false;
|
||||||
|
result["BTHome_version"] = _dib >> 5;
|
||||||
|
if (result["BTHome_version"] !== 2) return null;
|
||||||
|
//Can not handle encrypted data
|
||||||
|
if (result["encryption"]) return result;
|
||||||
|
buffer = buffer.slice(1);
|
||||||
|
|
||||||
|
let _bth;
|
||||||
|
let _value;
|
||||||
|
|
||||||
|
while (buffer.length > 0) {
|
||||||
|
_bth = BTH[buffer.at(0)];
|
||||||
|
if (typeof(_bth) === "undefined") {
|
||||||
|
console.log("BTH: unknown type");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer = buffer.slice(1);
|
||||||
|
_value = this.getBufValue(_bth.t, buffer);
|
||||||
|
|
||||||
|
if (_value === null) break;
|
||||||
|
if (typeof _bth.f !== "undefined") _value = _value * _bth.f;
|
||||||
|
result[_bth.n] = _value;
|
||||||
|
buffer = buffer.slice(getByteSize(_bth.t));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let ShellyBLUParser = {
|
||||||
|
getData: function (res) {
|
||||||
|
let result = BTHomeDecoder.unpack(res.service_data[BTHOME_SVC_ID_STR]);
|
||||||
|
result.addr = res.addr;
|
||||||
|
result.rssi = res.rssi;
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function sendToZoblak() {
|
||||||
|
const url = "http://agrar.zoblak.com/api/v1.0/sensorData";
|
||||||
|
const owner = "Controller: " + CONTROLLER_ID;
|
||||||
|
const payload = {
|
||||||
|
owner: owner,
|
||||||
|
temperatureValue: temperature[0],
|
||||||
|
humidityValue: humidity[0],
|
||||||
|
controllerId: CONTROLLER_ID,
|
||||||
|
temperatures: temperature
|
||||||
|
};
|
||||||
|
|
||||||
|
Shelly.call(
|
||||||
|
"HTTP.POST", {
|
||||||
|
"url": url,
|
||||||
|
"body": JSON.stringify(payload)
|
||||||
|
},
|
||||||
|
function(result) {
|
||||||
|
print("server says: ", result.code);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleScanResult(event, result) {
|
||||||
|
if(event === BLE.Scanner.SCAN_RESULT) {
|
||||||
|
for (var i in senzori) {
|
||||||
|
if(result.addr === senzori[i]) {
|
||||||
|
let data = ShellyBLUParser.getData(result);
|
||||||
|
temperature[i] = data.Temperature.toString();
|
||||||
|
humidity[i] = data.Humidity.toString();
|
||||||
|
sendToZoblak();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//BLE.Scanner.Subscribe(handleScanResult);
|
||||||
|
BLE.Scanner.Start({ duration_ms: SCAN_DURATION, active: false }, handleScanResult);
|
||||||
Reference in New Issue
Block a user