spejsiot/spejsiot/SpejsNode.cpp

219 lines
6.3 KiB
C++

#include <SpejsNode.h>
#include <Endpoint.h>
#include <ver.h>
#include <endpoints/ImplementationEndpoint.h>
#define CONFIG_FILE "config.json"
void SpejsNode::init(bool debug) {
deviceID = WifiStation.getMAC().substring(6, 12);
currentSlot = 0;
if(!rboot_get_last_boot_rom(&currentSlot)) {
currentSlot = rboot_get_current_rom();
}
Serial.begin(115200);
Serial.systemDebugOutput(debug); // Debug output to serial
debugf("*** SpejsNode init, runnning on: %s, current rom: %d", deviceID.c_str(), currentSlot);
WifiAccessPoint.enable(false);
WifiStation.enable(true);
WifiStation.config(WIFI_SSID, WIFI_PWD);
WifiEvents.onStationGotIP(StationGotIPDelegate(&SpejsNode::gotIP, this));
registerEndpoint("$implementation", new ImplementationEndpoint());
// Keepalive Timer initialization
keepaliveTimer.initializeMs(10000, TimerDelegate(&SpejsNode::keepAliveHandler, this)).start();
statusLED.high();
}
void SpejsNode::loadJSON(std::vector<EndpointInitializer> initializers) {
#ifdef RBOOT_SPIFFS_0
debugf("trying to mount spiffs at 0x%08x, length %d", RBOOT_SPIFFS_0, SPIFF_SIZE);
spiffs_mount_manual(RBOOT_SPIFFS_0, SPIFF_SIZE);
#else
debugf("trying to mount spiffs at 0x%08x, length %d", 0x100000, SPIFF_SIZE);
spiffs_mount_manual(0x100000, SPIFF_SIZE);
#endif
DynamicJsonBuffer jsonBuffer;
if (fileExist(CONFIG_FILE)) {
int size = fileGetSize(CONFIG_FILE);
debugf("Found config file, %d bytes", size);
char* jsonString = new char[size + 1];
fileGetContent(CONFIG_FILE, jsonString, size + 1);
JsonObject& root = jsonBuffer.parseObject(jsonString);
if (root.containsKey("name"))
deviceType = (root["name"]).asString();
JsonObject& data = root["endpoints"].asObject();
for (auto it: data) {
bool found = false;
for(auto init: initializers) {
Endpoint* ep = init(it.value);
if (ep != NULL) {
debugf("%s: got object", it.key);
registerEndpoint(it.key, ep);
found = true;
break;
}
}
if (!found) {
debugf("%s: nothing found", it.key);
}
}
} else {
debugf("No configuration");
}
}
void SpejsNode::keepAliveHandler() {
static int failureCounter = 0;
if(!WifiStation.isConnected()) {
statusLED.high();
debugf("keepalive: Network reconnect");
if(failureCounter++ < 5)
WifiStation.connect();
else
System.restart();
} else if(mqtt.getConnectionState() != eTCS_Connected) {
statusLED.high();
debugf("keepalive: MQTT reconnect");
if(failureCounter++ < 5)
onConnected();
else
System.restart();
} else {
statusLED.idle();
failureCounter = 0;
uint8_t mode;
if(rboot_get_last_boot_mode(&mode) && mode == MODE_TEMP_ROM) {
rboot_set_current_rom(currentSlot);
debugf("Successfuly connected, accepting temp rom");
}
}
}
inline String SpejsNode::DEV_TOPIC(String t) {
return TOPIC_PREFIX + deviceID + "/" + t;
}
void SpejsNode::httpIndex(HttpRequest &request, HttpResponse &response)
{
response.sendString("This is spejsiot device, take a look at: https://wiki.hackerspace.pl/projects:spejsiot\n"
"\nDevice type: " + deviceType +
"\nFirmware version: " + String(BUILD_ID) +
"\nMAC: " + WifiStation.getMAC());
}
/*
* Successful network connection handler
*/
void SpejsNode::gotIP(IPAddress ip, IPAddress netmask, IPAddress gateway) {
onConnected();
}
void SpejsNode::onConnected() {
statusLED.idle();
debugf("Connection successful");
// MQTT initialization
mqtt.setWill(DEV_TOPIC("$online"), "false", 1, true);
#ifdef ENABLE_SSL
const uint8_t sha1Fingerprint[] = SSL_FINGERPRINT;
mqtt.connect("iot-" + deviceID, "", "", true);
mqtt.addSslOptions(SSL_SERVER_VERIFY_LATER);
mqtt.setSslFingerprint(sha1Fingerprint, 20);
#else
mqtt.connect("iot-" + deviceID);
#endif
for(unsigned int i = 0 ; i < endpoints.count() ; i++) {
endpoints.valueAt(i)->onConnected();
}
subscribe("$implementation/+");
// Say hello
notify("$online", "true");
notify("$homie", "2");
notify("$name", deviceType);
notify("$localip", WifiStation.getIP().toString());
notify("$mac", WifiStation.getMAC());
notify("$fw/name", "spejsiot");
notify("$fw/version", BUILD_ID);
notify("$implementation/slot", String(currentSlot));
// HTTP initialization
http.listen(80);
http.addPath("/", HttpPathDelegate(&SpejsNode::httpIndex, this));
http.setDefaultHandler(HttpPathDelegate(&SpejsNode::httpFile, this));
}
void SpejsNode::httpFile(HttpRequest &request, HttpResponse &response)
{
String file = request.getPath();
if (file[0] == '/')
file = file.substring(1);
if (file.startsWith("api/1/")) {
String req = file.substring(6);
String key = req.substring(0, req.indexOf("/"));
String value = req.substring(req.indexOf("/") + 1);
if(key.length() == 0 || value.length() == 0 || !endpoints.contains(key)) {
response.code = 400;
} else {
EndpointResult result = endpoints[key]->onValue(key, value);
JsonObjectStream* stream = new JsonObjectStream();
JsonObject& json = stream->getRoot();
json["status"] = result.status;
response.sendJsonObject(stream);
}
}
}
/*
* Publish on device-specific topic
*/
bool SpejsNode::notify(String key, String value) {
mqtt.publish(DEV_TOPIC(key), value, true);
return mqtt.getConnectionState() == eTCS_Connected;
}
/*
* Subsribe to device-specific topic
*/
bool SpejsNode::subscribe(String topic) {
mqtt.subscribe(DEV_TOPIC(topic));
return mqtt.getConnectionState() == eTCS_Connected;
}
/*
* Register new endpoint
*/
void SpejsNode::registerEndpoint(String key, Endpoint* endpoint) {
endpoints[key] = endpoint;
endpoint->bind(key, this);
}
void SpejsNode::mqttCallback(String origtopic, String value) {
for(unsigned int i = 0 ; i < endpoints.count() ; i++) {
endpoints.valueAt(i)->onMessage(origtopic, value);
}
}