/* * configDTZ.h * File-class for DTZ-Data-Logger * for configuration of user-data * * author Creator P.Rebesky * Copyright (©): 2020-2032 by Peter Rebesky * This code can use in private cases only. Every business or companies using of this codes or codes parts is required an approval of us (me) * Every private use can exchange some parts of code or modify some code-lines. This code can change for private use only. * This software is basicly owned by Peter Rebesky and any comercial using is forbidden without approval of us (me). * Version 7.04 * 05.10.2023 */ #ifndef CONFIG_DTZ_H_ #define CONFIG_DTZ_H_ // class for manage config-data into and from file "config.ini" #include // LittleFS is declared #include "encryption.h" #ifdef SML_DIN #include "decodeMeterSML.h" #endif #ifdef ISKRA_MT174 #include "decodeMeterMT174.h" #endif #ifdef VDE_DIN #include "decodeMeterDIN.h" #endif #ifdef ZE311_DR #include "decodeMeterZE311.h" #endif #ifdef LG_E350 #include "decodeMeterE350.h" #endif #ifdef MBUS_DLMS #include "decodeMeterDLMS.h" #endif #ifdef PAF_EC3 #include "dMeterPAFec3.h" #endif #ifdef LG_E320 #include "decodeMeterE320.h" #endif #ifdef DIN_EBZ #include "decodeDIN_EBZ.h" #endif #define PostMmeName "XZmmen" #define PostUser "XZuser" #define PostPW "XZpw" #define PostID "XZid" #define PostHash "XZhash" #define PostUrl "XZurl" #define PostChecked "XZLEO" #define restday "restDay" #define wfactor "wFactor" #define meterKey "XZkey" #define mqttServer "MQTTserver" #define mqttUser "MQTTuser" #define mqttPassword "MQTTpw" #define mqttPort "MQTTport" #define mqttOn "XZmqON" #define HTTP "http://" #define HTTPS "https://" extern decodeMeter dMETER; extern encryptData eDATA; /***** class for save data intern into ESP8266 *****/ class configDTZ { private: String USER="User"; String PW="empty"; String TARGET_URL="meinzaehler.com/encrypt2dbase.php"; String NAMEDTZ="Zähler"; String MQTT_Broker="192.168.0.1"; String MQTT_User="User"; String MQTT_PW="user"; uint32_t MQTT_Port=1883; String HASHKEY; // its for encrypt password by encryptPW2Hash uint32_t USERID = 0; int RESTDAY = 0; int WFACTOR = 1; bool MOUNTED = false; void loadConfig(String s); int calculateFreeSpaceFS(); String decodeOnValue(String s); unsigned char h2int(char c); void encryptPW2Hash(); uint16_t encrypt_one_byte(uint16_t crypt, uint16_t oneByte, uint16_t poly); uint16_t reflect(uint16_t reflector, int bits); String getTargetWithout(); bool checkHTTPadjustTime(); public: int saveCounter = 0; int FREESPACE=0; int SHOWPROTOCOL = 1; // int RestDay=0; int SendSSL=0; // 0 = send over http, send over https int PushIntervall=15; // default value int SendMQTT=1; // if one then send data per mqtt. If zero dont send bool begin(); // start to get data from file String getUser(); String getPassw(); String getPasswEncrypt(); String getNameDTZ(); String getTargetURL(); String getTargetURLclean(); String getTargetServer(); String getIoBroker(); String getIoBrokerUser(); String getIoBrokerPW(); uint32_t getIoBrokerPort(); uint32_t getUserID(); bool readConfigDTZ(); bool saveConfigDTZ(); int getFreeSpace(); bool savePostValues(String saveValues); String getOnePostValue(String post,String ident); String getOneFileValue(String file,String ident); String urldecode(String str); String urlencode(String str); String readDir(); void saveValues(String str); String readSavedValues(int pointer); bool delFile(int pointer); int getRestDay(); int getFactor(); void setCRC_Check(bool onOff); void end(); }; /**** mount the file-object ****/ bool configDTZ::begin(){ if(LittleFS.begin()){ this->MOUNTED = true; this->readConfigDTZ(); this->delFile(0); // clear old saved data on file system this->calculateFreeSpaceFS(); } return this->MOUNTED; } /**** read values from file or create new default-values ****/ bool configDTZ::readConfigDTZ(){ bool r=false; File Fconfig = LittleFS.open("/config.ini","r"); if (Fconfig){ String s=Fconfig.readString(); this->loadConfig(s); Fconfig.close(); r = true; } return r; } /****** for compatibility ******/ void configDTZ::end(){ // do nothing, it's for compatibility only } /******** handle user **********/ String configDTZ::getUser(){ return this->USER; } /******** handle userID ********/ uint32_t configDTZ::getUserID(){ return this->USERID; } /******** handle password ******/ String configDTZ::getPassw(){ return this->PW; } String configDTZ::getPasswEncrypt(){ return this->HASHKEY; } /******** handle DTZ-name *******/ String configDTZ::getNameDTZ(){ return this->NAMEDTZ; } /******** handle target URL clean******/ String configDTZ::getTargetURLclean(){ return getTargetWithout(); } /******** handle target URL ******/ String configDTZ::getTargetURL(){ return this->TARGET_URL; } /****** handle update server ******/ String configDTZ::getTargetServer(){ String url = getTargetWithout(); int urlEnd = url.indexOf("/",0); return url.substring(0,urlEnd); } /******** handle ioBroker URL **/ String configDTZ::getIoBroker(){ return this->MQTT_Broker; } String configDTZ::getIoBrokerUser(){ return this->MQTT_User; } String configDTZ::getIoBrokerPW(){ return this->MQTT_PW; } uint32_t configDTZ::getIoBrokerPort(){ return this->MQTT_Port; } /******** handle day off *******/ int configDTZ::getRestDay(){ return this->RESTDAY; } /******** handle wandler factor *******/ int configDTZ::getFactor(){ return this->WFACTOR; } //*** get return without http r https ******/ String configDTZ::getTargetWithout(){ String url; int httpString = this->TARGET_URL.indexOf(HTTP,0); int httpsString = this->TARGET_URL.indexOf(HTTPS,0); if(httpString != -1){ url=TARGET_URL.substring(httpString+7); SendSSL=0; }else if(httpsString != -1){ url=TARGET_URL.substring(httpsString+8); SendSSL=1; }else{ url=this->TARGET_URL; SendSSL=0; } return url; } /*** end ***/ bool configDTZ::checkHTTPadjustTime(){ if(this->TARGET_URL.indexOf(HTTPS,0) >= 0) SendSSL=1; else SendSSL =0; if(SendSSL==1) _secAdjust=_ADJUST_HTTPS; else _secAdjust=_ADJUST_HTTP; return true; } /*** encryptPW2Hash by using user-name ***/ void configDTZ::encryptPW2Hash(){ char exHs[2]; // dummy for save value as HEX char resulthash[20]; // result-memory of 20 bytes (0-19) int16_t i_enc=0xffff; // start-byte for encyption / calculating this->HASHKEY = ""; int length_user = this->USER.length(); int length_user_count = 0; int length_pw = this->PW.length(); int length_pw_count = 0; for (int i=0; i<20; i++){ if (length_user_count >= length_user) length_user_count = 0; // if string end than start new until 20 bytes are full if (length_pw_count >= length_pw) length_pw_count = 0; // if string end than start new until 20 bytes are full resulthash[i] = (this->PW[length_pw_count] + this->USER[length_user_count])&0x00ff; // add pw and user until byte 20 i_enc = encrypt_one_byte(i_enc, resulthash[i], 0x1021); // calculate by polynom and drop bits for the first time resulthash[i] += i_enc & 0x00ff; // save result of calculation by add length_user_count ++; length_pw_count ++; // increment letter-counter for user and pw } for (int i=0; i<20; i++){ i_enc = encrypt_one_byte(i_enc, resulthash[i], 0x1021); // calculate by polynom and drop bits for the second time resulthash[i] = (resulthash[i] ^ i_enc) & 0x00ff; // save result of calculation by xor for the second time sprintf(exHs,"%02x",resulthash[i]); this->HASHKEY += exHs; } eDATA.setKey(resulthash); // set key into encryption class too! // eDATA.createUniqueFrame(getUser()); // and frame for future encryption } //**** create a one byte for hash encryption ****/ uint16_t configDTZ::reflect(uint16_t reflector, int bits){ // bits = how many bits should be reflected uint16_t shift=0; int i=0; do { shift <<= 1; if(reflector & 0x0001) shift |= 1; reflector >>= 1; i++; } while (i < bits); return shift; } uint16_t configDTZ::encrypt_one_byte(uint16_t crypt, uint16_t oneByte, uint16_t poly){ oneByte = reflect(oneByte,8); // reflected input oneByte <<= 8; // shift left eight times for(int i=0; i<8; i++){ if((crypt ^ oneByte) & 0x8000) crypt =(crypt << 1)^ poly; else crypt <<= 1; oneByte <<= 1; } return crypt; } /**** encryption end ***************/ void configDTZ::setCRC_Check(bool onOff){ // true = crc off, false = crc on if(onOff){ _CRC_CHECK=1; } else { _CRC_CHECK=0; } configDTZ::saveConfigDTZ(); } /**** save actuelle config-data ****/ bool configDTZ::saveConfigDTZ(){ bool r = false; String s = "{\n"; s += "user: \""; s += this->getUser(); s += "\",\n"; s += "pw: \""; s += this->getPassw(); s += "\",\n"; s += "userid: "; s += this->getUserID(); s += ",\n"; s += "url: \""; s += this->getTargetURL(); s += "\",\n"; s += "nameDTZ: \""; s += this->getNameDTZ(); s += "\",\n"; s += "MQTT_Broker: \""; s += this->MQTT_Broker; s += "\",\n"; s += "MQTT_Port: \""; s += this->MQTT_Port; s += "\",\n"; s += "MQTT_User: \""; s += this->MQTT_User; s += "\",\n"; s += "MQTT_PW: \""; s += this->MQTT_PW; s += "\",\n"; s += "SendMQTT: "; s += this->SendMQTT; s += ",\n"; #ifdef MBUS_DLMS s += "meterKey: \""; s += dMETER.getKey(); s += "\",\n"; #endif s += "showled: "; s += this->SHOWPROTOCOL; s += ",\n"; s += "restday: "; s += this->getRestDay(); s += ",\n"; s += "wfactor: "; s += this->getFactor(); s += ",\n"; s += "pushTime: "; s += this->PushIntervall; s += ",\n"; s += "crccheck: "; s += _CRC_CHECK; s += ",\n"; s += "}"; this->checkHTTPadjustTime(); // Serial.println(s); if(this->MOUNTED){ File Fconfig = LittleFS.open("/config.ini","w"); if (Fconfig){ int w=Fconfig.print(s); Fconfig.close(); if (w > 1) r = true; // write file was successful } } return r; } //******************* load one value from saved config ***************** String configDTZ::getOneFileValue(String file,String ident){ String ret="-1"; int argBegin = file.indexOf(ident); if(argBegin>0){ int argEnd = file.indexOf(",",argBegin+2); if(argEnd>0)ret = this->decodeOnValue(file.substring(argBegin, argEnd)); } return ret; } //******************* decode post-values ******************************* String configDTZ::getOnePostValue(String post,String ident){ String returnValue=""; if(post.length()>0){ int identBegin = post.indexOf(ident); if(identBegin >= 0){ int argEnd = post.indexOf("&",identBegin); if (argEnd == -1) argEnd = post.length(); returnValue = post.substring(post.indexOf("=",identBegin)+1,argEnd); } } return this->urldecode(returnValue); } /**** decode file-data and save into class ****/ void configDTZ::loadConfig(String s){ this->USER = getOneFileValue(s,"user:"); this->PW = getOneFileValue(s,"pw:"); this->TARGET_URL = getOneFileValue(s,"url:"); this->MQTT_Broker = getOneFileValue(s,"MQTT_Broker:"); this->MQTT_User = getOneFileValue(s,"MQTT_User:"); this->MQTT_PW = getOneFileValue(s,"MQTT_PW:"); this->MQTT_Port = getOneFileValue(s,"MQTT_Port:").toInt(); this->NAMEDTZ = getOneFileValue(s,"nameDTZ:"); #ifdef MBUS_DLMS dMETER.setKey(getOneFileValue(s,"meterKey:")); #endif this->USERID = (getOneFileValue(s,"userid:")).toInt(); this->SHOWPROTOCOL = (getOneFileValue(s,"showled:")).toInt(); this->SendMQTT = (getOneFileValue(s,"SendMQTT:")).toInt(); this->RESTDAY = (getOneFileValue(s,"restday:")).toInt(); this->WFACTOR = (getOneFileValue(s,"wfactor:")).toInt(); int pushTime = (getOneFileValue(s,"pushTime:")).toInt(); if(pushTime > 0 && pushTime <=59) this->PushIntervall=pushTime; else this->PushIntervall=15; _CRC_CHECK = (getOneFileValue(s,"crccheck:")).toInt(); this->checkHTTPadjustTime(); this->encryptPW2Hash(); // calculate hash } /**** extract one value from string ****/ String configDTZ::decodeOnValue(String s){ int valueBegin = s.indexOf("\""); int valueLength = s.length(); if(valueLength >0){ if(valueBegin > 0){ s = s.substring(valueBegin+1,valueLength-1); } else { valueBegin = s.indexOf(":"); s = s.substring(valueBegin+1,valueLength); } } else s = ""; return s; } /****** get free space from file-system ******/ int configDTZ::calculateFreeSpaceFS(){ if(MOUNTED){ FSInfo fsinfo; LittleFS.info(fsinfo); FREESPACE = fsinfo.totalBytes - fsinfo.usedBytes; } else FREESPACE = 0; return FREESPACE; } int configDTZ::getFreeSpace(){ return this->FREESPACE; } /***** decode values from html-post-arguments *****/ bool configDTZ::savePostValues(String saveValues){ bool r=false; if(saveValues.length()>0){ this->NAMEDTZ = getOnePostValue(saveValues,PostMmeName); this->USER = getOnePostValue(saveValues,PostUser); this->PW = getOnePostValue(saveValues,PostPW); this->USERID = (getOnePostValue(saveValues,PostID)).toInt(); this->TARGET_URL = getOnePostValue(saveValues,PostUrl); this->RESTDAY = getOnePostValue(saveValues,restday).toInt(); this->WFACTOR = getOnePostValue(saveValues,wfactor).toInt(); this->MQTT_Broker = getOnePostValue(saveValues,mqttServer); this->MQTT_User = getOnePostValue(saveValues,mqttUser); this->MQTT_PW = getOnePostValue(saveValues,mqttPassword); this->MQTT_Port = getOnePostValue(saveValues,mqttPort).toInt(); #ifdef MBUS_DLMS dMETER.setKey(getOnePostValue(saveValues,meterKey)); #endif if(saveValues.indexOf(PostChecked) >=0) SHOWPROTOCOL=1; else SHOWPROTOCOL=0; if(saveValues.indexOf(mqttOn) >=0) SendMQTT=1; else SendMQTT=0; } this->encryptPW2Hash(); // calculate new hash before save user-data // Serial.println(saveValues); return this->saveConfigDTZ(); } /***** url decoding part *****/ unsigned char configDTZ::h2int(char c) { if (c >= '0' && c <='9') return((unsigned char)c - '0'); if (c >= 'a' && c <='f') return((unsigned char)c - 'a' + 10); if (c >= 'A' && c <='F') return((unsigned char)c - 'A' + 10); return(0); } /***** url decoding function ******/ String configDTZ::urldecode(String str) { String encodedString=""; char c; char code0; char code1; for (int i =0; i < str.length(); i++){ c=str.charAt(i); if (c == '+') encodedString+=' '; else if (c == '%') { i++; code0=str.charAt(i); i++; code1=str.charAt(i); c = (h2int(code0) << 4) | h2int(code1); encodedString+=c; } else encodedString+=c; yield(); } return encodedString; } /***** url encoding function ******/ String configDTZ::urlencode(String str) { String encodedString=""; char c; char code0; char code1; char code2; for (int i =0; i < str.length(); i++){ c=str.charAt(i); if (c == ' ') encodedString+= '+'; else if (isalnum(c)) encodedString+=c; else{ code1=(c & 0xf)+'0'; if ((c & 0xf) >9) code1=(c & 0xf) - 10 + 'A'; c=(c>>4)&0xf; code0=c+'0'; if (c > 9) code0=c - 10 + 'A'; code2='\0'; encodedString+='%'; encodedString+=code0; encodedString+=code1; //encodedString+=code2; } yield(); } return encodedString; } void configDTZ::saveValues(String str){ if(this->MOUNTED && this->calculateFreeSpaceFS() > 4096){ // don't save more than avialable freespace this->saveCounter ++; String fileName = "/"; fileName += this->saveCounter; fileName += ".val"; File Fvalues = LittleFS.open(fileName,"w"); if (Fvalues){ int w=Fvalues.print(str); Fvalues.close(); } } else { if(!WiFi.isConnected()){ // check WiFi if connected yet ESP.restart(); // connect lost do restart } else delFile(0); // delete all saved files (saved protocols) } return; } String configDTZ::readSavedValues(int pointer){ String s= "*"; if(this->MOUNTED && this->saveCounter > 0 && pointer > 0){ String fileName = "/"; fileName += pointer; fileName += ".val"; File Fvalues = LittleFS.open(fileName,"r"); if (Fvalues){ s=Fvalues.readString(); Fvalues.close(); } } return s; } String configDTZ::readDir(){ String ret="Files:
"; Dir dir =LittleFS.openDir("/"); while (dir.next()){ ret += dir.fileName(); if(dir.fileSize()) { File f=dir.openFile("r"); ret+=" -> Size: "; ret+=f.size(); ret+=" Bytes
"; } else ret += "
"; } return ret; } bool configDTZ::delFile(int pointer){ // pointer = 0 -> del all files String fileName = "/"; if(pointer == 0){ for (int i=0;i<=_SAVE_PROT_MAX;i++){ fileName = "/"; fileName += i; fileName += ".val"; LittleFS.remove(fileName); this->saveCounter=0; if(i > _SAVE_PROT_MAX) break; } } else { fileName += pointer; fileName += ".val"; LittleFS.remove(fileName); } this->calculateFreeSpaceFS(); return false; } #endif //*** CONFIG_DTZ_H_