auth and change kmn

This commit is contained in:
Svante Kaiser 2024-01-10 19:04:40 +03:00
parent 48edd149de
commit 86d7343f64
33 changed files with 2795 additions and 2500 deletions

1
.gitignore vendored
View File

@ -6,3 +6,4 @@
build
node_modules
data/www

Binary file not shown.

Binary file not shown.

View File

@ -56,11 +56,11 @@ const IntercomSettingsForm: FC = () => {
</ValidatedTextField>
<ValidatedTextField
fieldErrors={fieldErrors}
name="firstAppartment"
name="firstApartment"
label="First Appartment"
fullWidth
variant="outlined"
value={data.firstAppartment}
value={data.firstApartment}
onChange={updateFormValue}
margin="normal"
/>

View File

@ -1,4 +1,4 @@
import { FC } from "react";
import { FC, useState } from "react";
import { Avatar, Box, Button, Divider, List, ListItem, ListItemAvatar, ListItemText, Theme, useTheme } from "@mui/material";
import PowerIcon from '@mui/icons-material/Power';
@ -9,12 +9,12 @@ import MeetingRoomIcon from '@mui/icons-material/MeetingRoom';
import QuestionMarkIcon from '@mui/icons-material/QuestionMark';
import NotificationsIcon from '@mui/icons-material/Notifications';
import RefreshIcon from '@mui/icons-material/Refresh';
import { useSnackbar } from "notistack";
import * as IntercomApi from "../../api/intercom";
import { DoorStatus, IntercomConnectionStatus, IntercomStatus, SwitchDoorType } from "../../types";
import { ButtonRow, FormLoader, SectionContent } from "../../components";
import { useRest } from "../../utils";
import {useSnackbar} from "notistack";
import { extractErrorMessage, useRest } from "../../utils";
export const intercomStatusHighlight = ({ status }: IntercomStatus, theme: Theme) => {
switch (status) {
@ -77,26 +77,35 @@ export const doorStatus = (status: DoorStatus) => {
}
};
export const openDoor = () => {
IntercomApi.switchDoor({
export const openDoorApi = async () => {
await IntercomApi.switchDoor({
type: SwitchDoorType.JOGGING,
status: DoorStatus.OPENED,
time: 1
})
.then(() => {
const { enqueueSnackbar } = useSnackbar();
enqueueSnackbar("Update successful", { variant: 'success' });
})
.catch((error) => {
console.log(error);
});
};
const IntercomStatusForm: FC = () => {
const { loadData, data, errorMessage } = useRest<IntercomStatus>({ read: IntercomApi.readIntercomStatus });
const { enqueueSnackbar } = useSnackbar();
const [confirmRestart, setConfirmRestart] = useState<boolean>(false);
const [processing, setProcessing] = useState<boolean>(false);
const theme = useTheme();
const openDoor = async () => {
setProcessing(true);
try {
await openDoorApi();
enqueueSnackbar("Door is opened", { variant: 'success' });
} catch (error: any) {
enqueueSnackbar(extractErrorMessage(error, 'Problem restarting device'), { variant: 'error' });
} finally {
setConfirmRestart(false);
setProcessing(false);
}
};
const content = () => {
if (!data) {
return (<FormLoader onRetry={loadData} errorMessage={errorMessage} />);

View File

@ -33,7 +33,7 @@ export interface IntercomJournal {
export interface IntercomSettings {
kmnModel: string;
kmnModelList: string[];
firstAppartment: number;
firstApartment: number;
}
export interface SwitchDoorDTO {

View File

@ -5,35 +5,10 @@ import { IntercomSettings } from '../types';
import { IP_ADDRESS_VALIDATOR } from './shared';
export const createIntercomSettingsValidator = (intercomSettings: IntercomSettings) => new Schema({
provision_mode: { required: true, message: "Please provide a provision mode" },
...({
ssid: [
{ required: true, message: "Please provide an SSID" },
{ type: "string", max: 32, message: "SSID must be 32 characters or less" }
],
password: [
{ required: true, message: "Please provide an access point password" },
{ type: "string", min: 8, max: 64, message: "Password must be 8-64 characters" }
],
channel: [
{ required: true, message: "Please provide a network channel" },
{ type: "number", message: "Channel must be between 1 and 14" }
],
max_clients: [
{ required: true, message: "Please specify a value for max clients" },
{ type: "number", min: 1, max: 9, message: "Max clients must be between 1 and 9" }
],
local_ip: [
{ required: true, message: "Local IP address is required" },
IP_ADDRESS_VALIDATOR
],
gateway_ip: [
{ required: true, message: "Gateway IP address is required" },
IP_ADDRESS_VALIDATOR
],
subnet_mask: [
{ required: true, message: "Subnet mask is required" },
IP_ADDRESS_VALIDATOR
]
})
kmnModel: [],
kmnModelList: [],
firstApartment: [
{ required: true, message: "Please provide a first appartment (for example, 1)" },
{ type: "number", message: "First appartment should be a number" }
]
});

File diff suppressed because it is too large Load Diff

View File

@ -24,3 +24,4 @@ lib_deps =
ottowinter/ESPAsyncWebServer-esphome@^3.1.0
bblanchon/ArduinoJson@^6.21.3
paulstoffregen/Time@^1.6.1
yutter/ArduinoJWT@^1.0.1

View File

@ -1,15 +1,20 @@
#include "app/routes.h"
#include "config/config.h"
#include "utils/print.h"
#include "utils/settings.h"
#include "utils/utils.h"
#include "utils/errorCodes.h"
#include "infra/eth.h"
#include "infra/httpServer.h"
#include "infra/mqtt.h"
#include "infra/relay.h"
#include "infra/fs.h"
#include "infra/intercom.h"
#include "domain/intercomJournal.h"
#include "domain/services/intercomJournal.h"
#include "domain/services/auth.h"
void handleDoorOpen(AsyncWebServerRequest *request) {
relayTurnOn();
@ -25,7 +30,7 @@ void getFeatures(AsyncWebServerRequest *request) {
JsonObject root = response->getRoot();
root["project"] = false;
root["security"] = false;
root["security"] = true;
root["mqtt"] = true;
root["ntp"] = false;
root["ota"] = false;
@ -47,25 +52,6 @@ void intercomStatus(AsyncWebServerRequest* request) {
request->send(response);
}
static void intercomSettingsRead(AsyncWebServerRequest* request) {
AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_INTERCOM_SETTINGS_SIZE);
JsonObject root = response->getRoot();
const size_t CAPACITY = JSON_ARRAY_SIZE(3);
StaticJsonDocument<CAPACITY> modelsDoc;
JsonArray models = modelsDoc.to<JsonArray>();
models.add("Vizit");
models.add("Cyfral");
root["kmnModel"] = "Vizit";
root["firstAppartment"] = 1;
root["kmnModelList"] = models;
response->setLength();
request->send(response);
}
static void intercomJournalRead(AsyncWebServerRequest* request) {
AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_INTERCOM_JOURNAL_SIZE);
JsonObject root = response->getRoot();
@ -82,8 +68,24 @@ static void intercomJournalRead(AsyncWebServerRequest* request) {
request->send(response);
}
static void intercomSettingsRead(AsyncWebServerRequest* request) {
AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_INTERCOM_SETTINGS_SIZE);
DynamicJsonDocument doc(1024);
bool success = readJsonVariantFromFile(INTERCOM_SETTINGS_PATH, doc);
JsonVariant root = doc.as<JsonVariant>();
if (!success)
getDefaultIntercomConf(root);
String jsonString;
serializeJson(root, jsonString);
request->send(200, "application/json", jsonString.c_str());
}
static void intercomSettingsUpdate(AsyncWebServerRequest *request,
uint8_t *data, size_t len, size_t index, size_t total) {
DynamicJsonDocument jsonDoc(MAX_INTERCOM_SETTINGS_SIZE);
String jsonStr = requestDataToStr(data, len);
DeserializationError error = deserializeJson(jsonDoc, jsonStr);
@ -95,7 +97,7 @@ uint8_t *data, size_t len, size_t index, size_t total) {
JsonVariant root = jsonDoc.as<JsonVariant>();
bool success = writeJsonVariantToFile(INTERCOM_SETTINGS_PATH, root);
bool success = writeJsonToFile(INTERCOM_SETTINGS_PATH, root);
if (success) {
String jsonString;
@ -106,8 +108,8 @@ uint8_t *data, size_t len, size_t index, size_t total) {
request->send(500, "text/plain", "Intercom settings not updated");
configureIntercom(
root["kmnModel"].as<String>(),
root["firstAppartment"].as<int>()
root["kmnModel"],
root["firstApartment"]
);
}
@ -173,6 +175,7 @@ static void networkSettingsRead(AsyncWebServerRequest* request) {
static void networkSettingsUpdate(AsyncWebServerRequest *request,
uint8_t *data, size_t len, size_t index, size_t total) {
DynamicJsonDocument jsonDoc(MAX_NETWORK_SETTINGS_SIZE);
String jsonStr = requestDataToStr(data, len);
DeserializationError error = deserializeJson(jsonDoc, jsonStr);
@ -184,7 +187,7 @@ uint8_t *data, size_t len, size_t index, size_t total) {
JsonVariant root = jsonDoc.as<JsonVariant>();
bool success = writeJsonVariantToFile(NETWORK_SETTINGS_PATH, root);
bool success = writeJsonToFile(NETWORK_SETTINGS_PATH, root);
if (success) {
String jsonString;
@ -235,6 +238,7 @@ static void mqttSettingsRead(AsyncWebServerRequest* request) {
static void mqttSettingsUpdate(AsyncWebServerRequest *request,
uint8_t *data, size_t len, size_t index, size_t total) {
DynamicJsonDocument jsonDoc(MAX_MQTT_SETTINGS_SIZE);
String jsonStr = requestDataToStr(data, len);
DeserializationError error = deserializeJson(jsonDoc, jsonStr);
@ -246,12 +250,9 @@ uint8_t *data, size_t len, size_t index, size_t total) {
JsonVariant root = jsonDoc.as<JsonVariant>();
bool fileLoaded = writeJsonVariantToFile(MQTT_SETTINGS_PATH, root);
bool fileLoaded = writeJsonToFile(MQTT_SETTINGS_PATH, root);
bool enabled = root["enabled"].as<bool>();
if (!loadMqttConfig())
println("Cannot load MQTT config");
bool mqttConfigured = configureMqtt(
root["enabled"].as<bool>(),
root["host"].as<String>(),
@ -322,6 +323,53 @@ void factoryReset(AsyncWebServerRequest *request) {
restartNow(request);
}
void verifyAuthorization(AsyncWebServerRequest *request) {
request->send(200, "text/plain", "OK");
}
static void signIn(AsyncWebServerRequest *request,
uint8_t *data, size_t len, size_t index, size_t total) {
DynamicJsonDocument jsonDoc(MAX_LOG_IN_SIZE);
String jsonStr = requestDataToStr(data, len);
println("Access token: ", jsonStr);
DeserializationError error = deserializeJson(jsonDoc, jsonStr);
if (!jsonDoc.is<JsonVariant>() || error) {
request->send(400);
return;
}
JsonVariant root = jsonDoc.as<JsonVariant>();
String accessToken;
StatusCode status = getAccessToken(
root["username"].as<String>(),
root["password"].as<String>(),
accessToken
);
println("Access token: ", accessToken);
if (status == StatusCode::NOT_FOUND) {
request->send(403, "text/plain", "Authorization failed");
}
if (!accessToken) {
request->send(500, "text/plain", "Access token not generated");
} else {
DynamicJsonDocument jsonDoc(MAX_LOG_IN_SIZE);
JsonVariant res = jsonDoc.as<JsonVariant>();
res["access_token"] = accessToken;
String jsonString;
serializeJson(res, jsonString);
println("Sign in response: ", jsonString);
request->send(200, "application/json", jsonString.c_str());
}
}
void initRoutes() {
AsyncWebServer& server = getServer();
server.on("/api/v1/door/open", handleDoorOpen);
@ -346,4 +394,7 @@ void initRoutes() {
server.on("/api/v1/systemStatus", systemStatus);
server.on("/api/v1/restart", restartNow);
server.on("/api/v1/factoryReset", factoryReset);
server.on("/api/v1/verifyAuthorization", HTTP_GET, verifyAuthorization);
server.on("/api/v1/signIn", HTTP_POST, [](AsyncWebServerRequest *request) {}, NULL, signIn);
}

View File

@ -16,7 +16,7 @@
#define SERIAL_NUMBER "4823"
#define DEFAULT_OUTPUT_TOPIC_PATH "/digitum/intercom_bridge4823/out/"
#define FACTORY_OUTPUT_TOPIC_PATH "/digitum/intercom_bridge4823/out/"
#define JSON_TOPIC_PATH "/digitum/intercom_bridge4823/out/json"
#define MAC_ADDRESS_MQTT_TOPIC "mac"
@ -48,8 +48,17 @@
#define MAX_MQTT_STATUS_SIZE 1024
#define MAX_MQTT_SETTINGS_SIZE 1024
#define MAX_ESP_STATUS_SIZE 1024
#define MAX_LOG_IN_SIZE 1024
#define FS_CONFIG_DIRECTORY "/config"
#define NETWORK_SETTINGS_PATH "/config/networkSettings.json"
#define INTERCOM_SETTINGS_PATH "/config/intercomSettings.json"
#define MQTT_SETTINGS_PATH "/config/mqttSettings.json"
#define MQTT_SETTINGS_PATH "/config/mqttSettings.json"
#define USERS_PATH "/config/users.json"
#define JWT_SECRET_KEY "secret"
#define FACTORY_ADMIN_USERNAME "admin"
#define FACTORY_ADMIN_PASSWORD "admin"
#define FACTORY_KMN_MODEL "Vizit"
#define FACTORY_FIRST_APARTMENT 1

View File

@ -0,0 +1,8 @@
#pragma once
#include <Arduino.h>
struct User {
String username;
bool admin;
};

View File

@ -0,0 +1,114 @@
#include "domain/repos/userRepo.h"
#include "config/config.h"
#include "infra/fs.h"
#define MAX_USERS_SIZE 10
#define USER_SIZE 1024
User _jsonVariantToUser(const JsonVariant& json) {
User user;
user.username = json["username"].as<String>();
user.admin = json["admin"].as<String>();
return user;
}
StatusCode createUser(User user) {
DynamicJsonDocument doc(USER_SIZE);
readJsonVariantFromFile(USERS_PATH, doc);
JsonArray root = doc.as<JsonArray>();
for (const JsonVariant& userObj: root)
if (userObj["username"] == user.username)
return StatusCode::CONFLICT;
DynamicJsonDocument userDoc(USER_SIZE);
JsonVariant userVariant = userDoc.as<JsonVariant>();
userVariant["username"] = user.username;
userVariant["admin"] = user.admin;
root.add(userVariant);
bool fileLoaded = writeJsonToFile(USERS_PATH, doc.as<JsonVariant>());
return fileLoaded ? StatusCode::OK : StatusCode::INTERNAL_SERVER_ERROR;
}
User getFactoryUser() {
return User{.username = FACTORY_ADMIN_USERNAME, .admin = FACTORY_ADMIN_PASSWORD};
}
User getUser(String username) {
DynamicJsonDocument doc(USER_SIZE);
if (!readJsonVariantFromFile(USERS_PATH, doc)) {
User factoryUser = getFactoryUser();
if (factoryUser.username == username)
return factoryUser;
else
return User();
}
JsonArray root = doc.as<JsonArray>();
for (const JsonVariant& userObj: root)
if (userObj["username"] == username)
return _jsonVariantToUser(userObj);
return User();
}
String getUsersJson() {
DynamicJsonDocument doc(USER_SIZE*MAX_USERS_SIZE);
if (!readJsonVariantFromFile(USERS_PATH, doc))
return "{}";
JsonArray root = doc.as<JsonArray>();
String jsonString;
serializeJson(root, jsonString);
return jsonString;
}
StatusCode deleteUser(String username) {
DynamicJsonDocument doc(USER_SIZE);
if (!readJsonVariantFromFile(USERS_PATH, doc))
return StatusCode::INTERNAL_SERVER_ERROR;
JsonArray root = doc.as<JsonArray>();
for (JsonArray::iterator it = root.begin(); it != root.end(); ++it) {
if ((*it)["username"] == username) {
root.remove(it);
bool fileLoaded = writeJsonToFile(USERS_PATH, doc.as<JsonVariant>());
return fileLoaded ? StatusCode::OK : StatusCode::INTERNAL_SERVER_ERROR;
}
}
return StatusCode::NOT_FOUND;
}
StatusCode updateUser(String username, User user) {
DynamicJsonDocument doc(USER_SIZE);
if (!readJsonVariantFromFile(USERS_PATH, doc))
return StatusCode::NOT_FOUND;
JsonArray root = doc.as<JsonArray>();
for (const JsonVariant& userObj: root) {
if (userObj["username"] == username) {
userObj["admin"] = user.admin;
bool fileLoaded = writeJsonToFile(USERS_PATH, doc.as<JsonVariant>());
return fileLoaded ? StatusCode::OK : StatusCode::INTERNAL_SERVER_ERROR;
}
}
return StatusCode::NOT_FOUND;
}

View File

@ -0,0 +1,13 @@
#pragma once
#include <Arduino.h>
#include "utils/errorCodes.h"
#include "domain/services/auth.h"
#include "domain/entities/user.h"
StatusCode createUser(User user);
User getUser(String username);
String getUsersJson();
StatusCode deleteUser(String username);
StatusCode updateUser(String username, User user);

View File

@ -0,0 +1,26 @@
#include "domain/services/auth.h"
#include "domain/entities/user.h"
#include "domain/repos/userRepo.h"
#include "config/config.h"
#include "utils/print.h"
ArduinoJWT jwtService(JWT_SECRET_KEY);
StatusCode getAccessToken(String username, String password, String& accessToken) {
DynamicJsonDocument jsonDoc(MAX_LOG_IN_SIZE);
JsonVariant root = jsonDoc.as<JsonVariant>();
User user = getUser(username);
if (user.username == "")
return StatusCode::NOT_FOUND;
root["username"] = username;
root["admin"] = user.admin;
String jsonString;
serializeJson(root, jsonString);
accessToken = jwtService.encodeJWT(jsonString);
return StatusCode::OK;
}

View File

@ -0,0 +1,9 @@
#pragma once
#include <Arduino.h>
#include <ArduinoJson.h>
#include <ArduinoJWT.h>
#include "utils/errorCodes.h"
StatusCode getAccessToken(String username, String password, String& accessToken);

View File

@ -1,4 +1,4 @@
#include "domain/intercomJournal.h"
#include "domain/services/intercomJournal.h"
#include "utils/print.h"
IntercomJournal _intercomJournal;

View File

@ -1,3 +1,5 @@
#pragma once
#include <Arduino.h>
#include <ArduinoJson.h>

View File

@ -1,4 +1,6 @@
#include "domain/stateMachineController.h"
#include "strategies/cyfralStrategy.h"
#include "strategies/vizitStrategy.h"
StateMachineController StateMachineController::instance;
StateMachineController& stateMachineController = StateMachineController::getInstance();
@ -42,4 +44,15 @@ int StateMachineController::getLastCalledNumber() {
DoorStatus getDoorStatus() {
return DoorStatus::CLOSED;
}
void StateMachineController::configure(KMNModel kmnModel, int firstApartment) {
switch (kmnModel) {
case KMNModel::CYFRAL:
this->setStrategy(new CyfralStrategy());
break;
case KMNModel::VIZIT:
this->setStrategy(new VizitStrategy());
break;
}
}

View File

@ -21,4 +21,6 @@ public:
IntercomConnectionStatus getStatus();
int getLastCalledNumber();
DoorStatus getDoorStatus();
void configure(KMNModel kmnModel, int firstApartment);
};

View File

@ -1,5 +1,5 @@
#include "domain/strategies/cyfralStrategy.h"
#include "domain/intercomJournal.h"
#include "domain/services/intercomJournal.h"
CyfralStrategy::CyfralStrategy() {}

View File

@ -1,5 +1,5 @@
#include "domain/strategies/vizitStrategy.h"
#include "domain/intercomJournal.h"
#include "domain/services/intercomJournal.h"
VizitStrategy::VizitStrategy() {}

View File

@ -116,12 +116,12 @@ void configureEth(JsonVariant& root) {
}
void getDefaultEthConf(JsonVariant& root) {
root["static_ip_config"] = true;
root["local_ip"] = ETH.localIP().toString();
root["gateway_ip"] = ETH.gatewayIP().toString();
root["subnet_mask"] = ETH.subnetMask().toString();
root["dns_ip"] = ETH.dnsIP().toString();
root["dns_ip_2"] = ETH.dnsIP(1).toString();
root["static_ip_config"] = true;
root["local_ip"] = ETH.localIP().toString();
root["gateway_ip"] = ETH.gatewayIP().toString();
root["subnet_mask"] = ETH.subnetMask().toString();
root["dns_ip"] = ETH.dnsIP().toString();
root["dns_ip_2"] = ETH.dnsIP(1).toString();
}
void getEthStatus(JsonObject& root) {
@ -162,30 +162,30 @@ bool loadEthConfig() {
}
void initEth() {
pinMode(NRST, OUTPUT);
digitalWrite(NRST, 0);
delay(200);
digitalWrite(NRST, 1);
delay(200);
digitalWrite(NRST, 0);
delay(200);
digitalWrite(NRST, 1);
delay(200);
pinMode(NRST, OUTPUT);
digitalWrite(NRST, 0);
delay(200);
digitalWrite(NRST, 1);
delay(200);
digitalWrite(NRST, 0);
delay(200);
digitalWrite(NRST, 1);
delay(200);
ETH.begin(ETH_ADDR,
ETH_POWER_PIN,
ETH_MDC_PIN,
ETH_MDIO_PIN,
ETH_TYPE,
ETH_CLK_MODE);
ETH.begin(ETH_ADDR,
ETH_POWER_PIN,
ETH_MDC_PIN,
ETH_MDIO_PIN,
ETH_TYPE,
ETH_CLK_MODE);
if (!loadEthConfig() && FACTORY_STATIC_LOCAL_IP) {
ETH.config(
FACTORY_STATIC_LOCAL_IP,
FACTORY_STATIC_GATEWAY,
FACTORY_STATIC_SUBNET
);
}
if (!loadEthConfig() && FACTORY_STATIC_LOCAL_IP) {
ETH.config(
FACTORY_STATIC_LOCAL_IP,
FACTORY_STATIC_GATEWAY,
FACTORY_STATIC_SUBNET
);
}
}
void testClient(const char * host, uint16_t port) {

View File

@ -50,7 +50,7 @@ bool createFolderIfNotExists(const char* file_path) {
return LittleFS.mkdir(folder);
}
bool writeJsonVariantToFile(const char* file_path, JsonVariant& jsonVariant) {
bool writeJsonToFile(const char* file_path, const JsonVariant& jsonValue) {
createFolderIfNotExists(file_path);
File file = LittleFS.open(file_path, "w");
@ -61,8 +61,8 @@ bool writeJsonVariantToFile(const char* file_path, JsonVariant& jsonVariant) {
}
String jsonString;
serializeJson(jsonVariant, jsonString);
println("Writing config: ", file_path, ", content:", jsonString);
serializeJson(jsonValue, jsonString);
println("Writing config: ", file_path, ", content: ", jsonString);
file.print(jsonString);
file.close();

View File

@ -4,5 +4,5 @@
void initFileSystem();
bool readJsonVariantFromFile(const char* filename, DynamicJsonDocument& jsonDoc);
bool writeJsonVariantToFile(const char* filename, JsonVariant& jsonObj);
bool writeJsonToFile(const char* file_path, const JsonVariant& jsonValue);
void deleteFilesInDir(const char* path);

View File

@ -1,9 +1,48 @@
#include "infra/intercom.h"
#include "infra/fs.h"
#include "utils/print.h"
extern StateMachineController& stateMachineController;
void configureIntercom(String kmnModel, int firstAppartment) {
void configureIntercom(String kmnModel, int firstApartment) {
KMNModel model;
if (kmnModel == "Vizit") {
model = KMNModel::VIZIT;
} else if (kmnModel == "Cyfral") {
model = KMNModel::CYFRAL;
} else {
println("Wrong intercom model: ", kmnModel);
return;
}
stateMachineController.configure(model, firstApartment);
println("Intercom configured: ", kmnModel, ", first appartment: ", firstApartment);
}
bool loadIntercomConfig() {
DynamicJsonDocument doc(1024);
bool success = readJsonVariantFromFile(INTERCOM_SETTINGS_PATH, doc);
if (!success)
return false;
JsonVariant root = doc.as<JsonVariant>();
configureIntercom(root["kmnModel"], root["firstApartment"]);
return true;
}
void initIntercom() {
if (!loadIntercomConfig() && FACTORY_STATIC_LOCAL_IP) {
configureIntercom(
FACTORY_KMN_MODEL,
FACTORY_FIRST_APARTMENT
);
}
}
IntercomConnectionStatus getIntercomStatus() {
@ -13,3 +52,20 @@ IntercomConnectionStatus getIntercomStatus() {
int getLastCalledNumber() {
return stateMachineController.getLastCalledNumber();
}
JsonArray getIntercomModels() {
const size_t CAPACITY = JSON_ARRAY_SIZE(3);
StaticJsonDocument<CAPACITY> modelsDoc;
JsonArray models = modelsDoc.to<JsonArray>();
models.add("Vizit");
models.add("Cyfral");
return models;
}
void getDefaultIntercomConf(JsonVariant& root) {
root["kmnModel"] = FACTORY_KMN_MODEL;
root["firstApartment"] = FACTORY_FIRST_APARTMENT;
root["kmnModelList"] = getIntercomModels();
}

View File

@ -1,6 +1,11 @@
#include <Arduino.h>
#include <ArduinoJson.h>
#include "domain/stateMachineController.h"
void configureIntercom(String kmnModel, int firstAppartment);
void configureIntercom(String kmnModel, int firstApartment);
bool loadIntercomConfig();
void initIntercom();
IntercomConnectionStatus getIntercomStatus();
int getLastCalledNumber();
int getLastCalledNumber();
void getDefaultIntercomConf(JsonVariant& root);

View File

@ -8,10 +8,9 @@ AsyncMqttClient mqttClient;
TimerHandle_t mqttReconnectTimer;
AsyncMqttClientDisconnectReason mqttDisconnectReason;
DynamicJsonDocument mqttConf(1024);
String mqttOutputJson = "";
bool mqttConnected = false;
bool mqttEnabled = true;
bool mqttEnabled = false;
// Pointers to hold retained copies of the mqtt client connection strings.
// This is required as AsyncMqttClient holds refrences to the supplied connection strings.
@ -36,7 +35,7 @@ void publishJSONToMQTT(const char* topic, T message) {
}
const char* getFullTopic(const char* topic) {
return (String(DEFAULT_OUTPUT_TOPIC_PATH) + String(topic)).c_str();
return (String(FACTORY_OUTPUT_TOPIC_PATH) + String(topic)).c_str();
}
void publishToMQTT(const char* topic, const char* message) {
@ -245,16 +244,14 @@ void getDefaultMqttConf(JsonVariant& root) {
}
bool loadMqttConfig() {
bool success = readJsonVariantFromFile(MQTT_SETTINGS_PATH, mqttConf);
if (!success)
return false;
}
void reconnectMQTTIfNeeded() {
if (!mqttClient.connected()) {
if (mqttConf.isNull())
loadMqttConfig();
DynamicJsonDocument mqttConf(1024);
if (!readJsonVariantFromFile(MQTT_SETTINGS_PATH, mqttConf))
return;
JsonVariant root = mqttConf.as<JsonVariant>();

View File

@ -21,7 +21,6 @@ AsyncMqttClient& getMqttClient();
AsyncMqttClientDisconnectReason& getMqttDisconnectReason();
bool getMqttConnected();
bool getMqttEnabled();
bool loadMqttConfig();
bool configureMqtt(
bool enabled,
String host,

View File

@ -4,6 +4,7 @@
enum class SwitchDoorType { ON_OFF, JOGGING, DELAY };
enum class DoorStatus { OPENED, CLOSED };
enum class KMNModel { CYFRAL, VIZIT };
void relayTurnOn();
void relayTurnOff();

View File

@ -15,6 +15,7 @@
#include "infra/relay.h"
#include "infra/fs.h"
#include "infra/httpServer.h"
#include "infra/intercom.h"
#include "app/routes.h"
@ -31,7 +32,6 @@ int zeros = 0;
int ones = 0;
extern StateMachineController& stateMachineController;
CyfralStrategy strategy;
void IRAM_ATTR one() {
flag = true;
@ -71,8 +71,7 @@ void setup() {
initMQTT();
initRoutes();
initHttpServer();
stateMachineController.setStrategy(&strategy);
initIntercom();
}
void loop() {

8
src/utils/errorCodes.h Normal file
View File

@ -0,0 +1,8 @@
#pragma once
enum class StatusCode {
OK,
NOT_FOUND,
CONFLICT,
INTERNAL_SERVER_ERROR
};

View File

@ -42,7 +42,7 @@ String Time::getFormattedTimeAgo(unsigned long timeMillis) const {
struct tm *timeStruct = gmtime(&currentTime);
if (timeStruct->tm_yday >= 0) {
if (timeStruct->tm_yday > 0) {
return String(timeStruct->tm_yday) + " days ago";
} else if (timeStruct->tm_hour > 0) {
return String(timeStruct->tm_hour) + " hours ago";