/* Loxmock - Mocked Loxone functions for functional development/testing on console-level Andrej Konkow, 2026/04 */ #define LOXMOCK_VERSION "v01" #define CONSOLE #define TRUE 1 printf("###### loxmock %s ######\n", LOXMOCK_VERSION); /*************************************************** Mocked time functions including a standard helper structure as struct tm seems not to be accessible */ struct my_tm { int tm_sec; // seconds int tm_min; // minutes (0-59) int tm_hour; // hours (0-23) int tm_mday; // days of month (1-31) int tm_mon; // months since January (0-11) int tm_year; // years since 1900 int tm_wday; // weekdays since Sunday (0-6) int tm_yday; // days since Jan 1. (0-365) int tm_isdst; // summertime-flag (<0: unknown, 0: winter, >0: summer) }; unsigned int getcurrenttime() { return time(NULL); } // returns UTC time int gethour( unsigned int pTime, int local) { int value = pTime / 60 / 60 % 24 + (local ? LOXMOCK_LOCAL_TZDIFF_TO_UTC : 0); void *p[3] = { &pTime, &local, &value }; lm_printf("gethour(%d, %d): %d\n", p); return value; } int getminute(unsigned int pTime, int local) { int value = pTime / 60 % 60; void *p[3] = { &pTime, &local, &value }; lm_printf("getminute(%d, %d): %d\n", p); return value; } int getsecond(unsigned int pTime, int local) { int value = pTime % 60; void *p[3] = { &pTime, &local, &value }; lm_printf("getsecond(%d, %d): %d\n", p); return value; } int getmonth( unsigned int pTime, int local) { struct my_tm *ts = lm_getTimeStruct(pTime, local); int value = ts->tm_mon+1; void *p[3] = { &pTime, &local, &value }; lm_printf("getmonth(%d, %d): %d\n", p); return value; } int getday( unsigned int pTime, int local) { struct my_tm *ts = lm_getTimeStruct(pTime, local); int value = ts->tm_mday; void *p[3] = { &pTime, &local, &value }; lm_printf("getday(%d, %d): %d\n", p); return value; } int getyear( unsigned int pTime, int local) { struct my_tm *ts = lm_getTimeStruct(pTime, local); int value = ts->tm_year+1900; void *p[3] = { &pTime, &local, &value }; lm_printf("getyear(%d, %d): %d\n", p); return value; } struct my_tm *lm_getTimeStruct(unsigned int pTime, int local) { struct my_tm *ts = NULL; int time = (int)pTime; if (local == 1) ts = (struct my_tm *)localtime(&time); else ts = (struct my_tm *)gmtime(&time); if (ts != NULL) return ts; // Most likely error 22 printf("\n**** --> Runtime error %d at getting systemtime structure. No influence on this. :-( Just restart script an try again...\n", errno ); exit(1); } unsigned int convertutc2local(unsigned int timeutc) { return gettimeval(getyear(timeutc ,0), getmonth(timeutc ,0), getday(timeutc ,0), gethour(timeutc ,0), getminute(timeutc ,0), getsecond(timeutc ,0), 1); } unsigned int convertlocal2utc(unsigned int timelocal) { return gettimeval(getyear(timelocal,1), getmonth(timelocal,1), getday(timelocal,1), gethour(timelocal,1), getminute(timelocal,1), getsecond(timelocal,1), 0); } unsigned int gettimeval(int year,int month,int day,int hour,int minutes,int seconds,int local) { struct my_tm *mytm = (struct my_tm *)malloc(sizeof(struct my_tm)); memset(mytm, 0, sizeof(struct my_tm)); mytm->tm_sec = seconds; mytm->tm_min = minutes; mytm->tm_hour = (local ? hour + LOXMOCK_LOCAL_TZDIFF_TO_UTC : hour); mytm->tm_mday = day; mytm->tm_mon = month-1; mytm->tm_year = year-1900; mytm->tm_isdst = LOXMOCK_LOCAL_IS_SUMMERTIME; unsigned int genTime = (unsigned int)mktime((struct tm*)mytm); free(mytm); void *p[9] = { "gettimeval", &year, &month, &day, &hour, &minutes, &seconds, &local, &genTime }; lm_printf("%s(%d,%d,%d,%d,%d,%d,%d), Returning: %d\n", p); return genTime; } void sleeps(int sec) { sleep(sec * 1000); } void sleep (int ms) // Pseudosleep implementation { int now = time(NULL), targettimeMin = now + ms/1000; void *p[2] = { "sleep", &ms }; if (! LOXMOCK_EXECUTE_SLEEP) { lm_printf("%s(%dms), Not waiting in mock...\n", p); return; } lm_printf("%s(%dms), Waiting...\n", p); while (TRUE) for (int i=0; i<10000; i++) // Pseudo-Delay if (time(NULL) >= targettimeMin) return; } /*************************************************** Mocked streaming functions */ enum lm_streamtype { lm_ascii, lm_hexbytes }; typedef struct STREAM { int streamtype; char *resultString; char *resultBytes; int resultBytesLen; int bytesRead; } STREAM; STREAM *stream_create(char* filename,int read,int append) { void *p[4] = { "stream_create", filename, &read, &append }; lm_printf("%s(\"%s\",%d,%d)\n", p); STREAM *s = (STREAM*)malloc(sizeof(STREAM)); memset(s, 0, sizeof(STREAM)); s->resultString = loxmock_result_stream; s->streamtype = LOXMOCK_STREAM; if (s->streamtype == lm_hexbytes) { s->resultBytes = (char *)calloc(strlen(s->resultString)/2+1, sizeof(char)); s->resultBytesLen = lm_convertHexToBytes(s->resultString, s->resultBytes); } else { s->resultBytes = loxmock_result_stream; s->resultBytesLen = strlen(loxmock_result_stream); } return s; }; int stream_write(STREAM* stream,void* ptr,int size) { char *dest = (char *)calloc(3*size, sizeof(char)); lm_convertToReadableHex(ptr, size, dest, 0, 1); void *p[2] = { &size, dest }; lm_printf("stream_write() is writing %d bytes on stream(non flushing) --> %s\n", p); free(dest); return size; } int stream_read (STREAM* stream,void* ptr,int size,int timeout) { void *p[3] = { "stream_read()", &size, &timeout }; lm_printf("%s(stream, bytePtr, %d, %d)\n", p); return lm_stream_read(stream, ptr, size, 0); } int stream_readline(STREAM* stream,void* ptr,int maxsize,int timeout) { void *p[3] = { "stream_readline()", &maxsize, &timeout }; lm_printf("%s(stream, bytePtr, %d, %d)\n", p); return lm_stream_read(stream, ptr, maxsize, 1); } void stream_printf(STREAM* stream,char *format, ...) { void *p[2] = { "stream_printf()", format }; lm_printf("%s, format: %s\n", p); } void stream_flush(STREAM* stream) { void *p[1] = { "stream_flush()" }; lm_printf("%s\n", p); } void stream_close(STREAM* stream) { void *p[1] = { "stream_close()" }; lm_printf("%s\n", p); if (stream == NULL) { printf("\n**** --> stream is NULL. Please check...\n"); return; } if (stream->streamtype == lm_hexbytes) free(stream->resultBytes); free(stream); } int lm_stream_read(STREAM* stream,void* ptr,int size, int toEol) { int i, readCnt = 0; char *cPtr = (char *)ptr; for (i=0; ibytesRead < stream->resultBytesLen; i++) { cPtr[i] = stream->resultBytes[stream->bytesRead]; stream->bytesRead++; readCnt++; if (toEol && cPtr[i] == '\n') { i++; break; } } if (stream->streamtype == lm_ascii) cPtr[i] = 0; char *printBuf = NULL; void *p[2] = { &readCnt }; if (stream->streamtype == lm_ascii) { p[1] = cPtr; } else { printBuf = (char *)calloc(3*readCnt+1, sizeof(char)); lm_convertToReadableHex(cPtr, readCnt, printBuf, 0, 1); p[1] = printBuf; } lm_printf("--> %d bytes read from stream: %s\n", p); if (printBuf != NULL) free(printBuf); return readCnt; } /*************************** Input and Output functions */ void setlogtext (char *str) { void *p[2] = { "setlogtext()", str }; lm_printf("%s, string: %s\n", p); } void setoutputtext(int channel, char *msg) { void *p[4] = { "setoutputtext()", (channel > 2 ? "channel out of bounds" : "channel"), &channel, msg }; lm_printfF("%s, %s: %d, string: %s\n", 1, p); } void setoutput (int channel, float value) { void *p[4] = { "setoutput()", (channel > 12 ? "channel out of bounds" : "channel"), &channel, &value }; if (ceil(value) != value) { lm_printfF("%s, %s: %d, float: %f\n", 1, p); return; } int iValue = (int) value; p[3] = &iValue; lm_printfF("%s, %s: %d, int: %d\n", 1, p); } char *getinputtext (int channel) { char *retValue; if (channel > 2) retValue = "\"\" (channel out of scope)"; else retValue = loxmock_in_char[channel]; void *p[3] = { "getinputtext", &channel, retValue }; lm_printf("%s(%d), returning: %s\n", p); if (channel > 2) return ""; return strdup(loxmock_in_char[channel]); } // returns the defined values from above float getinput(int channel) { float retValue; char *remark; if (channel > 12) { retValue = 0; remark = " (channel out of scope)"; } else { retValue = loxmock_in_float[channel]; remark = ""; } void *p[4] = { "getinput", &channel, &retValue, remark }; lm_printf("%s(%d), returning: %f%s\n", p); return retValue; } float getio(char *str) { float retValue = 0; int found = 0; loxmock_virtual_block *vb; for (int i=0; ikey, str) == 0) { retValue = vb->value; found = 1; break; } } void *p[4] = { "getio", str , &found, &retValue }; lm_printf("%s(\"%s\"), block found: %d, returning: %f\n", p); return retValue; } int setio(char *str,float value) { int found = 0; loxmock_virtual_block *vb; for (int i=0; ikey, str) == 0) { vb->value = value; found = 1; break; } } void *p[5] = { "setio", str , &value, &found, &found }; lm_printf("%s(\"%s\", %f), value set: %d, returning: %d\n", p); return found; } char *httpget(char *address, char *page) { void *p[3] = { "httpget", address, page }; lm_printf("%s(), http://%s%s - Returning: \n", p); return strdup(loxmock_result_httpget); } char *localwebservice(char *str) { void *p[2] = { "localwebservice", str }; lm_printf("%s(\"%s\") - Returning: \n", p); return strdup(loxmock_result_localwebservice); } int getinputevent() { char *bm = loxmox_inputevent_bitmask; int result = 0; for (; *bm; bm++) { result = result << 1; if (*bm != '0') result++; } void *p[2] = { "getinputevent()", &result }; lm_printf("%s, Setting back to 0, but now returning: %d\n", p); memset(loxmox_inputevent_bitmask, 0, strlen(loxmox_inputevent_bitmask)); return result; } /* Rest */ int batoi(char *v) { char *o = v; int r = 0; if (v != NULL) { while (*v == ' ') v++; r = atoi(v); } void *p[3] = { "batoi", o, &r }; lm_printf("%s(\"%s\"), returning %d\n", p); return r; } float batof(char *v) { char *o = v; float r = 0; if (v != NULL) { while (*v == ' ') v++; r = atof(v); } void *p[3] = { "batof", o, &r }; lm_printf("%s(\"%s\"), returning %f\n", p); return r; } int lineno() { void *p[1] = { "lineno" }; lm_printf("%s(), Not supported, just mocked. Returning: 0\n", p); return 0; } int getcpuinfo() { void *p[1] = { "getcpuinfo" }; lm_printf("%s(), Not supported, just mocked. returning: 0\n", p); return 0; } int getheapusage() { void *p[1] = { "getheapusage" }; lm_printf("%s(), Not supported, just mocked. returning: 0\n", p); return 0; } int getmaxheap() { void *p[1] = { "getmaxheap" }; lm_printf("%s(), Not supported, just mocked. returning: 0\n", p); return 0; } int getspsstatus() { void *p[1] = { "getspsstatus" }; lm_printf("%s(), Not supported, just mocked. returning: 0\n", p); return 0; } char *getprogramname() { void *p[2] = { "getprogramname()", "loxmock" }; lm_printf("%s, returning: %s\n", p); return p[1]; } void errorprintf(char *format, ...) { void *p[2] = { "errorprintf()", format }; lm_printf("%s, format: %s\n", p); } char *tmpnam(char *str) { char *tmpFilename = (str == NULL ? "mockedTmpFilename" : str); void *p[3] = { "tmpnam", str, tmpFilename }; lm_printf("%s(\"%s\"), Returning: %s\n", p); return tmpFilename; } char *index(char *searchString, int aChar) { char szBuf[2] = { aChar, 0 }; char *result = strstr(searchString, szBuf); void *p[4] = { "index", searchString, szBuf, (result == NULL ? "NULL" : result) }; lm_printf("%s(\"%s\", %s), returning: %s\n", p); return result; } char* strstrskip(char *str,char *strfind) { char *result = strstr(str, strfind); if (result != NULL) result += strlen(strfind); void *p[4] = { "strstrskip", str, strfind, (result == NULL ? "NULL" : result) }; lm_printf("%s(\"%s\", \"%s\"), returning: %s\n", p); return result; } int strfind(char *str,char *strfind,int pos) { int oPos = pos; char *oStr = str; while (*str && pos>0) { str++; pos--; } char *result = strstr(str, strfind); if (result == NULL) return -1; int position = result - str + oPos; void *p[5] = { "strfind", oStr, strfind, &pos, &position }; lm_printf("%s(\"%s\", \"%s\", %d), Returning: %d\n", p); return position; } char *strdup(char *string) { void *p[2] = { "strdup()", string }; lm_printf("--> %s - reminder to free() it: %s\n", p); int len = strlen(string); char *newString = (char *)calloc(len+1, sizeof(char)); memcpy(newString, string, len); return newString; } char *getxmlvalue(char *str,int index,char* name) { int x = -1, oIndex = index; char *value = NULL; // Emulate the passionless implementation of Loxone including the bugs... :-( // - the searchstring just appends the two characters =" to the end of the parameter "name", ignoring potential whitespaces etc. // - if name is not found (x==-1) then return a string from the beginning with the length of the adjusted searchstring (developer must have been really cheap) // --> and of course blindly search for the next quotation marks, ignoring quotings // - In Loxone you don't even need the complete attribute name, just the appropriate suffix... applause, applause int namelen = strlen(name); char *searchBuf = (char *)calloc(namelen+3, sizeof(char)); memcpy(searchBuf, name, namelen); searchBuf[namelen] = '='; searchBuf[namelen+1] = '\"'; int searchBufLen = strlen(searchBuf); x = strfind(str, searchBuf, 0); for (; index > 0; index--) { x = strfind(str, searchBuf, x+searchBufLen); index--; } free(searchBuf); // Ignore x == -1 and implement the bug int y = strfind(str, "\"",x+searchBufLen); char *resultBuf = (char *)calloc(y-x-searchBufLen+1, sizeof(char)); memcpy(resultBuf, &str[x+searchBufLen], y-x-searchBufLen); void *p[5] = { "getxmlvalue", str, &oIndex, name, resultBuf }; lm_printf("%s(\"%s\", %d, \"%s\"), remember to free() it, Returning: %s\n", p); return resultBuf; } /* Convert characters/bytes to readable hex-string Caution: Don't use strlen or other string-oriented functions as byte-characters don't behave like normal alphabetical characters */ void lm_convertToReadableHex(char *bytes, int byteLen, char *dest, int hasContent, int printWhitespace) { for (int i=0; i hexlen) // corrupt bytestring, unhandled errorcase break; if (hexString[i] == ' ') // Ignore whitespaces { i--; continue; } // stick together the two 4bit values resultBytes[resultcounter] = (lm_MapHex2Int[hexString[i]] << 4) | (lm_MapHex2Int[hexString[i+1]] & 0xF); resultcounter++; } return resultcounter; } /* Due to variadic printf-function we have to handle the printf on our own when centralizing */ void lm_printf(char *myP, void **params) { lm_printfF(myP, 0, params); } void lm_printfF(char *myP, int forceOutput, void **params) { if (! LOXMOCK_PRINTCALLS && ! forceOutput) return; char specifier[20]; int specifierIdx, paramsIdx = 0; // print the output prefix printf("[loxmock] "); while (*myP != 0) { if (*myP != '%') { printf("%c", *myP); myP++; continue; } memset(specifier, 0, 20); specifierIdx = 0; do { specifier[specifierIdx++] = *myP; myP++; } while (! lm_MapIsPrintfSpecifier[*myP]); specifier[specifierIdx++] = *myP; // specifier switch (*myP) { case 'u': case 'o': case 'x': case 'X': printf(specifier, *((unsigned int*)(params[paramsIdx]))); break; case 'f': case 'F': case 'e': case 'E': case 'g': case 'G': case 'a': case 'A': printf(specifier, *((float*)(params[paramsIdx]))); break; case 'd': case 'i': printf(specifier, *((int*) (params[paramsIdx]))); break; case 'c': printf(specifier, (char) params[paramsIdx]); break; case 's': printf(specifier, (char*) params[paramsIdx]); break; case 'p': printf(specifier, (void*) params[paramsIdx]); break; case '%': printf("%%"); paramsIdx--; break; case 'n': // case n is not working properly in picoC with argument, so just: no print break; } paramsIdx++; myP++; } } // #include "loxmock_test.c"