Controlling Fritz DECT 200 aktors through fritz.Box API

Einklappen
X
 
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge
  • guido4096
    Dumb Home'r
    • 11.10.2015
    • 18

    #1

    Controlling Fritz DECT 200 aktors through fritz.Box API

    Just in case somebody is interested. I've developed in PicoC the protocol to control Fritz DECT200 aktors with the Fritz API.
    This API cannot be controlled using the regular HTTP Virtual Output commands, because it requires calculating a sessionid.
    The sessionid can be retrieved from the fritz.box, but requires calculating the MD5 hash over a challenge, username and password.

    Hence, no possibility to do this wih plan HTTP Virtual Output commands.

    The PicoC program I developed contains the MD5 hash calculation ported to PicoC. Which was not straightforward, due to a bug in PicoC in the rsh operator on unsigned int.

    Anyway, got it to work, if somebody is interested just let me know.
  • guido4096
    Dumb Home'r
    • 11.10.2015
    • 18

    #2
    For those who are interested, see the code below to drive the Fritz Dect 200 acuators from the Loxone environment.

    A few things to note:
    - You have to set the username and password. Towards the bottom of the file.
    - You have to set the AIN of your actuators. See ain1 and ain2 towards the bottom of the file.
    - The code is there to drive 2 actuators, you can add more as needed
    - The first two digital inputs of the programming block are used to switch AIN 1 or AIN 2 to on/off

    - The AIN is an identification id of the actuators, which you can retrieve through the fritzbox.
    - The code only supports the later versions of fritz.
    - When you did not define users in fritz.box, but just have a password, set the USERNAME as empty -> “”

    For more information on the fritz api: https://avm.de/fileadmin/user_upload...-Interface.pdf.
    There you see also commands like “getswitchpower” and “getswitchenergy”.

    Coding is fairly straighforward if you are used to C or C++ coding. But there are some annoying things:
    - const is not supported
    - don’t put TAB characs in the text, the loxone crashes

    Hope this helps,

    Guido


    // MD5 calculations
    unsigned int F(unsigned int x, unsigned int y, unsigned int z)
    {
    return (z ^ (x & (y ^ z)));
    }
    unsigned int G(unsigned int x, unsigned int y, unsigned int z)
    {
    return (y ^ (z & (x ^ y)));
    }
    unsigned int H(unsigned int x, unsigned int y, unsigned int z)
    {
    return ((x ^ y) ^ z);
    }
    unsigned int H2(unsigned int x, unsigned int y, unsigned int z)
    {
    return (x ^ (y ^ z));
    }
    unsigned int I(unsigned int x, unsigned int y, unsigned int z)
    {
    return (y ^ (x | ~z));
    }
    unsigned int rsh(unsigned int a, unsigned int s)
    {
    return a >> s;
    // rsh operator is broken on loxone picoc on version 7.1.x and before.
    // It pulls in 1's instead of 0's. Use this code to workaround.
    //unsigned int r = a >> 1;
    //r = r & 0x7fffffff;
    //return r >> (s - 1);
    }

    unsigned int STEPF(unsigned int a, unsigned int b, unsigned int c, unsigned int d, unsigned int x, unsigned int t, unsigned int s)
    {
    a += F(b, c, d) + x + t;
    a = ((a << s) | rsh(a & 0xffffffff, (32 - s)));
    a += b;
    return a;
    }
    unsigned int STEPG(unsigned int a, unsigned int b, unsigned int c, unsigned int d, unsigned int x, unsigned int t, unsigned int s)
    {
    a += G(b, c, d) + x + t;
    a = ((a << s) | rsh(a & 0xffffffff, (32 - s)));
    a += b;
    return a;
    }
    unsigned int STEPH(unsigned int a, unsigned int b, unsigned int c, unsigned int d, unsigned int x, unsigned int t, unsigned int s)
    {
    a += H(b, c, d) + x + t;
    a = ((a << s) | rsh(a & 0xffffffff, (32 - s)));
    a += b;
    return a;
    }
    unsigned int STEPH2(unsigned int a, unsigned int b, unsigned int c, unsigned int d, unsigned int x, unsigned int t, unsigned int s)
    {
    a += H2(b, c, d) + x + t;
    a = ((a << s) | rsh(a & 0xffffffff, (32 - s)));
    a += b;
    return a;
    }
    unsigned int STEPI(unsigned int a, unsigned int b, unsigned int c, unsigned int d, unsigned int x, unsigned int t, unsigned int s)
    {
    a += I(b, c, d) + x + t;
    a = ((a << s) | rsh(a & 0xffffffff, (32 - s)));
    a += b;
    return a;
    }
    struct MD5_CTX {
    unsigned int lo;
    unsigned int hi;
    unsigned int a;
    unsigned int b;
    unsigned int c;
    unsigned int d;
    unsigned char buffer[64];
    unsigned int block[16];
    };
    unsigned int GET(struct MD5_CTX* ctx, int n)
    {
    return ctx->block[n];
    }
    unsigned int SET(struct MD5_CTX* ctx, unsigned char *ptr, int n)
    {
    ctx->block[n] = (unsigned int)ptr[n * 4] |
    ((unsigned int)ptr[n * 4 + 1] << 8) |
    ((unsigned int)ptr[n * 4 + 2] << 16) |
    ((unsigned int)ptr[n * 4 + 3] << 24) ;
    return ctx->block[n];
    }
    void *body(struct MD5_CTX *ctx, void *data, unsigned long size)
    {
    unsigned char *ptr;
    unsigned int a;
    unsigned int b;
    unsigned int c;
    unsigned int d;
    unsigned int saved_a;
    unsigned int saved_b;
    unsigned int saved_c;
    unsigned int saved_d;

    ptr = (unsigned char *)data;

    a = ctx->a;
    b = ctx->b;
    c = ctx->c;
    d = ctx->d;

    do {
    saved_a = a;
    saved_b = b;
    saved_c = c;
    saved_d = d;

    // Round 1
    a=STEPF( a, b, c, d, SET(ctx,ptr,0), 0xd76aa478, 7);
    d=STEPF( d, a, b, c, SET(ctx,ptr,1), 0xe8c7b756, 12);
    c=STEPF( c, d, a, b, SET(ctx,ptr,2), 0x242070db, 17);
    b=STEPF( b, c, d, a, SET(ctx,ptr,3), 0xc1bdceee, 22);
    a=STEPF( a, b, c, d, SET(ctx,ptr,4), 0xf57c0faf, 7);
    d=STEPF( d, a, b, c, SET(ctx,ptr,5), 0x4787c62a, 12);
    c=STEPF( c, d, a, b, SET(ctx,ptr,6), 0xa8304613, 17);
    b=STEPF( b, c, d, a, SET(ctx,ptr,7), 0xfd469501, 22);
    a=STEPF( a, b, c, d, SET(ctx,ptr,8), 0x698098d8, 7);
    d=STEPF( d, a, b, c, SET(ctx,ptr,9), 0x8b44f7af, 12);
    c=STEPF( c, d, a, b, SET(ctx,ptr,10), 0xffff5bb1, 17);
    b=STEPF( b, c, d, a, SET(ctx,ptr,11), 0x895cd7be, 22);
    a=STEPF( a, b, c, d, SET(ctx,ptr,12), 0x6b901122, 7);
    d=STEPF( d, a, b, c, SET(ctx,ptr,13), 0xfd987193, 12);
    c=STEPF( c, d, a, b, SET(ctx,ptr,14), 0xa679438e, 17);
    b=STEPF( b, c, d, a, SET(ctx,ptr,15), 0x49b40821, 22);


    // Round 2
    a=STEPG( a, b, c, d, GET(ctx,1), 0xf61e2562, 5);
    d=STEPG( d, a, b, c, GET(ctx,6), 0xc040b340, 9);
    c=STEPG( c, d, a, b, GET(ctx,11), 0x265e5a51, 14);
    b=STEPG( b, c, d, a, GET(ctx,0), 0xe9b6c7aa, 20);
    a=STEPG( a, b, c, d, GET(ctx,5), 0xd62f105d, 5);
    d=STEPG( d, a, b, c, GET(ctx,10), 0x02441453, 9);
    c=STEPG( c, d, a, b, GET(ctx,15), 0xd8a1e681, 14);
    b=STEPG( b, c, d, a, GET(ctx,4), 0xe7d3fbc8, 20);
    a=STEPG( a, b, c, d, GET(ctx,9), 0x21e1cde6, 5);
    d=STEPG( d, a, b, c, GET(ctx,14), 0xc33707d6, 9);
    c=STEPG( c, d, a, b, GET(ctx,3), 0xf4d50d87, 14);
    b=STEPG( b, c, d, a, GET(ctx,8), 0x455a14ed, 20);
    a=STEPG( a, b, c, d, GET(ctx,13), 0xa9e3e905, 5);
    d=STEPG( d, a, b, c, GET(ctx,2), 0xfcefa3f8, 9);
    c=STEPG( c, d, a, b, GET(ctx,7), 0x676f02d9, 14);
    b=STEPG( b, c, d, a, GET(ctx,12), 0x8d2a4c8a, 20);

    // Round 3
    a=STEPH( a, b, c, d, GET(ctx,5), 0xfffa3942, 4);
    d=STEPH2( d, a, b, c, GET(ctx,8), 0x8771f681, 11);
    c=STEPH( c, d, a, b, GET(ctx,11), 0x6d9d6122, 16);
    b=STEPH2( b, c, d, a, GET(ctx,14), 0xfde5380c, 23);
    a=STEPH( a, b, c, d, GET(ctx,1), 0xa4beea44, 4);
    d=STEPH2( d, a, b, c, GET(ctx,4), 0x4bdecfa9, 11);
    c=STEPH( c, d, a, b, GET(ctx,7), 0xf6bb4b60, 16);
    b=STEPH2( b, c, d, a, GET(ctx,10), 0xbebfbc70, 23);
    a=STEPH( a, b, c, d, GET(ctx,13), 0x289b7ec6, 4);
    d=STEPH2( d, a, b, c, GET(ctx,0), 0xeaa127fa, 11);
    c=STEPH( c, d, a, b, GET(ctx,3), 0xd4ef3085, 16);
    b=STEPH2( b, c, d, a, GET(ctx,6), 0x04881d05, 23);
    a=STEPH( a, b, c, d, GET(ctx,9), 0xd9d4d039, 4);
    d=STEPH2( d, a, b, c, GET(ctx,12), 0xe6db99e5, 11);
    c=STEPH( c, d, a, b, GET(ctx,15), 0x1fa27cf8, 16);
    b=STEPH2( b, c, d, a, GET(ctx,2), 0xc4ac5665, 23);

    // Round 4
    a=STEPI( a, b, c, d, GET(ctx,0), 0xf4292244, 6);
    d=STEPI( d, a, b, c, GET(ctx,7), 0x432aff97, 10);
    c=STEPI( c, d, a, b, GET(ctx,14), 0xab9423a7, 15);
    b=STEPI( b, c, d, a, GET(ctx,5), 0xfc93a039, 21);
    a=STEPI( a, b, c, d, GET(ctx,12), 0x655b59c3, 6);
    d=STEPI( d, a, b, c, GET(ctx,3), 0x8f0ccc92, 10);
    c=STEPI( c, d, a, b, GET(ctx,10), 0xffeff47d, 15);
    b=STEPI( b, c, d, a, GET(ctx,1), 0x85845dd1, 21);
    a=STEPI( a, b, c, d, GET(ctx,8), 0x6fa87e4f, 6);
    d=STEPI( d, a, b, c, GET(ctx,15), 0xfe2ce6e0, 10);
    c=STEPI( c, d, a, b, GET(ctx,6), 0xa3014314, 15);
    b=STEPI( b, c, d, a, GET(ctx,13), 0x4e0811a1, 21);
    a=STEPI( a, b, c, d, GET(ctx,4), 0xf7537e82, 6);
    d=STEPI( d, a, b, c, GET(ctx,11), 0xbd3af235, 10);
    c=STEPI( c, d, a, b, GET(ctx,2), 0x2ad7d2bb, 15);
    b=STEPI( b, c, d, a, GET(ctx,9), 0xeb86d391, 21);

    a += saved_a;
    b += saved_b;
    c += saved_c;
    d += saved_d;

    ptr += 64;
    } while (size -= 64);

    ctx->a = a;
    ctx->b = b;
    ctx->c = c;
    ctx->d = d;

    return ptr;
    }

    void MD5_Init(struct MD5_CTX *ctx)
    {
    ctx->a = 0x67452301;
    ctx->b = 0xefcdab89;
    ctx->c = 0x98badcfe;
    ctx->d = 0x10325476;

    ctx->lo = 0;
    ctx->hi = 0;
    }
    void MD5_Update(struct MD5_CTX *ctx, void *data, unsigned long size)
    {
    unsigned int saved_lo;
    unsigned long used;
    unsigned long available;

    saved_lo = ctx->lo;
    if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo)
    ctx->hi++;
    ctx->hi += size >> 29;

    used = saved_lo & 0x3f;

    if (used) {
    available = 64 - used;

    if (size < available) {
    memcpy(&ctx->buffer[used], data, size);
    return;
    }

    memcpy(&ctx->buffer[used], data, available);
    data = (unsigned char *)data + available;
    size -= available;
    body(ctx, ctx->buffer, 64);
    }

    if (size >= 64) {
    data = body(ctx, data, size & ~(unsigned long)0x3f);
    size &= 0x3f;
    }

    memcpy(ctx->buffer, data, size);
    }
    void MD5_Final(unsigned char *result, struct MD5_CTX *ctx)
    {
    unsigned long used;
    unsigned long available;

    used = ctx->lo & 0x3f;

    ctx->buffer[used++] = 0x80;

    available = 64 - used;

    if (available < 8) {
    memset(&ctx->buffer[used], 0, available);
    body(ctx, ctx->buffer, 64);
    used = 0;
    available = 64;
    }

    memset(&ctx->buffer[used], 0, available - 8);

    ctx->lo <<= 3;
    ctx->buffer[56] = ctx->lo;
    ctx->buffer[57] = rsh(ctx->lo, 8);
    ctx->buffer[58] = rsh(ctx->lo, 16);
    ctx->buffer[59] = rsh(ctx->lo, 24);
    ctx->buffer[60] = ctx->hi;
    ctx->buffer[61] = rsh(ctx->hi, 8);
    ctx->buffer[62] = rsh(ctx->hi, 16);
    ctx->buffer[63] = rsh(ctx->hi, 24);

    body(ctx, ctx->buffer, 64);

    result[0] = ctx->a;
    result[1] = rsh(ctx->a , 8);
    result[2] = rsh(ctx->a , 16);
    result[3] = rsh(ctx->a , 24);
    result[4] = ctx->b;
    result[5] = rsh(ctx->b , 8);
    result[6] = rsh(ctx->b , 16);
    result[7] = rsh(ctx->b , 24);
    result[8] = ctx->c;
    result[9] = rsh(ctx->c , 8);
    result[10] = rsh(ctx->c , 16);
    result[11] = rsh(ctx->c , 24);
    result[12] = ctx->d;
    result[13] = rsh(ctx->d , 8);
    result[14] = rsh(ctx->d , 16);
    result[15] = rsh(ctx->d , 24);

    memset(ctx, 0, sizeof(*ctx));
    }

    // Convert 16 byte MD5 hash into printable HEX encoded string
    void MD5_CONVERT(char* iMD5, char* oMD5_A)
    {
    for (int i = 0; i < 16; i++)
    {
    sprintf(oMD5_A, "%02x", iMD5[i]);
    oMD5_A += 2;
    }
    *oMD5_A = 0;
    }


    // simple (meaning not foolproof) implementation to get the value of an xml element.
    char* GetStringXMLValue(char* xml, char* element, char* buffer)
    {
    char* retval = 0;
    char* v = strstrskip(xml, element);
    if (v!=0)
    {
    char* closing=index(v, '<');
    if (closing != 0)
    {
    *closing=0;
    strcpy(buffer,v);
    *closing='<';
    retval=buffer;
    }
    }
    return retval;
    }

    char* GetResponse(char* c, char* p, char* buffer)
    {
    // Struct to do MD5 calculations
    struct MD5_CTX ctx;

    char HA1_TEXT[1000]; HA1_TEXT[0] = 0;
    sprintf(HA1_TEXT, "%s-%s", c, p);

    // Convert according to UTF-16LE
    // Assume we use no non-acii characs when converting char* to UTF-16LE
    int l = strlen(HA1_TEXT);
    char* c1 = (char*)HA1_TEXT+2*l;
    char* c2 = (char*)HA1_TEXT+l;
    int i=l;

    c1[1] = 0;
    c1[2] = 0;
    while (i>=0)
    {
    c1[0] = *c2;
    c1[1] = 0;
    c1 -= 2;
    c2 -= 1;
    i--;
    }
    MD5_Init(&ctx);
    MD5_Update(&ctx, HA1_TEXT, l*2);
    char HA1_MD5[16];
    MD5_Final((unsigned char*)HA1_MD5, &ctx);
    char HA1_MD5_A[32 + 1];
    MD5_CONVERT((unsigned char*)HA1_MD5, HA1_MD5_A);
    sprintf(buffer, "%s-%s", c, HA1_MD5_A);
    return buffer;
    }

    char* GetSID(char* host, char* username, char* password, char* buffer)
    {
    char* rval = 0;
    char cmd[200];cmd[0]=0;
    sprintf(cmd,"/login_sid.lua?sid=%s", buffer);
    char* r1 = httpget(host, cmd);
    if (r1!=0)
    {
    char* sid=GetStringXMLValue(r1, "<SID>", buffer);
    if (sid != 0)
    {
    if(strcmp(sid, "0000000000000000") == 0)
    {
    char c_buffer[100];
    char* c=GetStringXMLValue(r1, "<Challenge>", c_buffer);
    char r_buffer[1000]; r_buffer[0]=0;
    char* r=GetResponse(c, password, r_buffer);

    cmd[0]=0;
    sprintf(cmd, "/login_sid.lua?username=%s&response=%s",username,r) ;
    char* r2=httpget(host, cmd);
    if (r2!=0)
    {
    char* sid2=GetStringXMLValue(r2, "<SID>", buffer);
    if (sid2 != 0 && strcmp(sid2, "0000000000000000") != 0)
    {
    rval=sid2;
    }
    free(r2);
    }
    }
    else
    {
    //printf("SID already valid\n");
    rval=sid;
    }
    }
    free(r1);
    }
    if (rval == 0)
    buffer[0]=0;
    else
    printf("SID voor FritizBox is=%s\n", rval);
    return rval;
    }

    void Switch(char* host, char* ain, char* sid, float i)
    {
    char cmd[200];
    if (i > 0)
    sprintf(cmd, "/webservices/homeautoswitch.lua?ain=%s&switchcmd=setswitchon&si d=%s", ain, sid);
    else
    sprintf(cmd, "/webservices/homeautoswitch.lua?ain=%s&switchcmd=setswitchoff&s id=%s", ain, sid);
    char* res = httpget(host, cmd);
    if (res != 0)
    {
    //printf("res=%s\n", res);
    free(res);
    }
    }

    // Example to check if an SID is valid
    // http://192.168.178.1/login_sid.lua?sid=xxx

    // Example to get a session id
    // http://192.168.178.1/login_sid.lua?u...=&response=xxx

    // Example to get list of ain's
    // http://192.168.178.1/webservices/hom...chlist&sid=xxx

    // Switch AIN on or off, using AIN id and session id
    // http://192.168.178.1/webservices/hom...itchon&sid=xxx

    // http://192.168.178.1/webservices/hom...itchon&sid=xxx

    // Username host and password
    #define PASSWORD "xxx!"
    #define USERNAME ""
    #define HOST "192.168.178.1"

    // AIN 1
    // AIN 2
    char* ain1="xxx";
    char* ain2="xxx";

    // Get the SID
    char sid_buffer[100]; sid_buffer[0]=0;
    char* sid = GetSID(HOST, USERNAME, PASSWORD, sid_buffer);
    unsigned int lastrefresh_sid = getcurrenttime();


    int analoginput1 = 0x2;
    int analoginput2 = 0x4;
    int analoginput3 = 0x6;

    while (TRUE)
    {
    // Refresh the SID every 60 seconds
    unsigned int currenttime = getcurrenttime();
    if (currenttime - lastrefresh_sid > 60)
    {
    sid = GetSID(HOST, USERNAME, PASSWORD, sid_buffer);
    lastrefresh_sid=currenttime;
    }
    int nEvents = getinputevent();
    if ( (nEvents & 0xe) )
    {
    //printf("e=%d\n", nEvents);
    // First process the actual event to improve responsiveness
    if (nEvents & analoginput1)
    {
    Switch(HOST, ain1, sid, getinput(0));
    //printf("ain1 %d\n", getinput(0));
    }
    if (nEvents & analoginput2)
    {
    Switch(HOST, ain2, sid, getinput(1));
    //printf("ain2 %d\n", getinput(1));
    }

    }
    sleep(30);
    }

    Kommentar

    • guido4096
      Dumb Home'r
      • 11.10.2015
      • 18

      #3
      Ps watch out with the rsh implementation. It is only working on beta firmware. On older versions, use the workaround instead!

      Kommentar

      • rolandpj
        Azubi
        • 16.07.2016
        • 7

        #4
        Hi Guido,

        thanks for that great script. I used it (especially the SID part) and enhanced it:

        - Output 1: Shows if Fritz Dect 200 is present
        - Output 2: Shows status of Switch (on/off)
        - Output 3: current energy consumption (W)
        - Output 4: Energy consumption total (kwh)
        - Output 5: Temperature °C

        Please not that the input parameter seems to be wrong in current Loxone 7.4.4.14. The First input is the text input instead the digital input.

        have fun!

        // https://www.loxforum.com/forum/germa...-fritz-box-api
        // http://www.loxone.com/dede/service/d.../programm.html
        // https://avm.de/fileadmin/user_upload...-Interface.pdf
        // write program here in PicoC
        // MD5 calculations

        unsigned int F(unsigned int x, unsigned int y, unsigned int z)
        {
        return (z ^ (x & (y ^ z)));
        }
        unsigned int G(unsigned int x, unsigned int y, unsigned int z)
        {
        return (y ^ (z & (x ^ y)));
        }
        unsigned int H(unsigned int x, unsigned int y, unsigned int z)
        {
        return ((x ^ y) ^ z);
        }
        unsigned int H2(unsigned int x, unsigned int y, unsigned int z)
        {
        return (x ^ (y ^ z));
        }
        unsigned int I(unsigned int x, unsigned int y, unsigned int z)
        {
        return (y ^ (x | ~z));
        }
        unsigned int rsh(unsigned int a, unsigned int s)
        {
        return a >> s;
        // rsh operator is broken on loxone picoc on version 7.1.x and before.
        // It pulls in 1's instead of 0's. Use this code to workaround.
        //unsigned int r = a >> 1;
        //r = r & 0x7fffffff;
        //return r >> (s - 1);
        }

        unsigned int STEPF(unsigned int a, unsigned int b, unsigned int c, unsigned int d, unsigned int x, unsigned int t, unsigned int s)
        {
        a += F(b, c, d) + x + t;
        a = ((a << s) | rsh(a & 0xffffffff, (32 - s)));
        a += b;
        return a;
        }
        unsigned int STEPG(unsigned int a, unsigned int b, unsigned int c, unsigned int d, unsigned int x, unsigned int t, unsigned int s)
        {
        a += G(b, c, d) + x + t;
        a = ((a << s) | rsh(a & 0xffffffff, (32 - s)));
        a += b;
        return a;
        }
        unsigned int STEPH(unsigned int a, unsigned int b, unsigned int c, unsigned int d, unsigned int x, unsigned int t, unsigned int s)
        {
        a += H(b, c, d) + x + t;
        a = ((a << s) | rsh(a & 0xffffffff, (32 - s)));
        a += b;
        return a;
        }
        unsigned int STEPH2(unsigned int a, unsigned int b, unsigned int c, unsigned int d, unsigned int x, unsigned int t, unsigned int s)
        {
        a += H2(b, c, d) + x + t;
        a = ((a << s) | rsh(a & 0xffffffff, (32 - s)));
        a += b;
        return a;
        }
        unsigned int STEPI(unsigned int a, unsigned int b, unsigned int c, unsigned int d, unsigned int x, unsigned int t, unsigned int s)
        {
        a += I(b, c, d) + x + t;
        a = ((a << s) | rsh(a & 0xffffffff, (32 - s)));
        a += b;
        return a;
        }
        struct MD5_CTX {
        unsigned int lo;
        unsigned int hi;
        unsigned int a;
        unsigned int b;
        unsigned int c;
        unsigned int d;
        unsigned char buffer[64];
        unsigned int block[16];
        };
        unsigned int GET(struct MD5_CTX* ctx, int n)
        {
        return ctx->block[n];
        }
        unsigned int SET(struct MD5_CTX* ctx, unsigned char *ptr, int n)
        {
        ctx->block[n] = (unsigned int)ptr[n * 4] |
        ((unsigned int)ptr[n * 4 + 1] << 8) |
        ((unsigned int)ptr[n * 4 + 2] << 16) |
        ((unsigned int)ptr[n * 4 + 3] << 24) ;
        return ctx->block[n];
        }
        void *body(struct MD5_CTX *ctx, void *data, unsigned long size)
        {
        unsigned char *ptr;
        unsigned int a;
        unsigned int b;
        unsigned int c;
        unsigned int d;
        unsigned int saved_a;
        unsigned int saved_b;
        unsigned int saved_c;
        unsigned int saved_d;

        ptr = (unsigned char *)data;

        a = ctx->a;
        b = ctx->b;
        c = ctx->c;
        d = ctx->d;

        do {
        saved_a = a;
        saved_b = b;
        saved_c = c;
        saved_d = d;

        // Round 1
        a=STEPF( a, b, c, d, SET(ctx,ptr,0), 0xd76aa478, 7);
        d=STEPF( d, a, b, c, SET(ctx,ptr,1), 0xe8c7b756, 12);
        c=STEPF( c, d, a, b, SET(ctx,ptr,2), 0x242070db, 17);
        b=STEPF( b, c, d, a, SET(ctx,ptr,3), 0xc1bdceee, 22);
        a=STEPF( a, b, c, d, SET(ctx,ptr,4), 0xf57c0faf, 7);
        d=STEPF( d, a, b, c, SET(ctx,ptr,5), 0x4787c62a, 12);
        c=STEPF( c, d, a, b, SET(ctx,ptr,6), 0xa8304613, 17);
        b=STEPF( b, c, d, a, SET(ctx,ptr,7), 0xfd469501, 22);
        a=STEPF( a, b, c, d, SET(ctx,ptr,8), 0x698098d8, 7);
        d=STEPF( d, a, b, c, SET(ctx,ptr,9), 0x8b44f7af, 12);
        c=STEPF( c, d, a, b, SET(ctx,ptr,10), 0xffff5bb1, 17);
        b=STEPF( b, c, d, a, SET(ctx,ptr,11), 0x895cd7be, 22);
        a=STEPF( a, b, c, d, SET(ctx,ptr,12), 0x6b901122, 7);
        d=STEPF( d, a, b, c, SET(ctx,ptr,13), 0xfd987193, 12);
        c=STEPF( c, d, a, b, SET(ctx,ptr,14), 0xa679438e, 17);
        b=STEPF( b, c, d, a, SET(ctx,ptr,15), 0x49b40821, 22);


        // Round 2
        a=STEPG( a, b, c, d, GET(ctx,1), 0xf61e2562, 5);
        d=STEPG( d, a, b, c, GET(ctx,6), 0xc040b340, 9);
        c=STEPG( c, d, a, b, GET(ctx,11), 0x265e5a51, 14);
        b=STEPG( b, c, d, a, GET(ctx,0), 0xe9b6c7aa, 20);
        a=STEPG( a, b, c, d, GET(ctx,5), 0xd62f105d, 5);
        d=STEPG( d, a, b, c, GET(ctx,10), 0x02441453, 9);
        c=STEPG( c, d, a, b, GET(ctx,15), 0xd8a1e681, 14);
        b=STEPG( b, c, d, a, GET(ctx,4), 0xe7d3fbc8, 20);
        a=STEPG( a, b, c, d, GET(ctx,9), 0x21e1cde6, 5);
        d=STEPG( d, a, b, c, GET(ctx,14), 0xc33707d6, 9);
        c=STEPG( c, d, a, b, GET(ctx,3), 0xf4d50d87, 14);
        b=STEPG( b, c, d, a, GET(ctx,8), 0x455a14ed, 20);
        a=STEPG( a, b, c, d, GET(ctx,13), 0xa9e3e905, 5);
        d=STEPG( d, a, b, c, GET(ctx,2), 0xfcefa3f8, 9);
        c=STEPG( c, d, a, b, GET(ctx,7), 0x676f02d9, 14);
        b=STEPG( b, c, d, a, GET(ctx,12), 0x8d2a4c8a, 20);

        // Round 3
        a=STEPH( a, b, c, d, GET(ctx,5), 0xfffa3942, 4);
        d=STEPH2( d, a, b, c, GET(ctx,8), 0x8771f681, 11);
        c=STEPH( c, d, a, b, GET(ctx,11), 0x6d9d6122, 16);
        b=STEPH2( b, c, d, a, GET(ctx,14), 0xfde5380c, 23);
        a=STEPH( a, b, c, d, GET(ctx,1), 0xa4beea44, 4);
        d=STEPH2( d, a, b, c, GET(ctx,4), 0x4bdecfa9, 11);
        c=STEPH( c, d, a, b, GET(ctx,7), 0xf6bb4b60, 16);
        b=STEPH2( b, c, d, a, GET(ctx,10), 0xbebfbc70, 23);
        a=STEPH( a, b, c, d, GET(ctx,13), 0x289b7ec6, 4);
        d=STEPH2( d, a, b, c, GET(ctx,0), 0xeaa127fa, 11);
        c=STEPH( c, d, a, b, GET(ctx,3), 0xd4ef3085, 16);
        b=STEPH2( b, c, d, a, GET(ctx,6), 0x04881d05, 23);
        a=STEPH( a, b, c, d, GET(ctx,9), 0xd9d4d039, 4);
        d=STEPH2( d, a, b, c, GET(ctx,12), 0xe6db99e5, 11);
        c=STEPH( c, d, a, b, GET(ctx,15), 0x1fa27cf8, 16);
        b=STEPH2( b, c, d, a, GET(ctx,2), 0xc4ac5665, 23);

        // Round 4
        a=STEPI( a, b, c, d, GET(ctx,0), 0xf4292244, 6);
        d=STEPI( d, a, b, c, GET(ctx,7), 0x432aff97, 10);
        c=STEPI( c, d, a, b, GET(ctx,14), 0xab9423a7, 15);
        b=STEPI( b, c, d, a, GET(ctx,5), 0xfc93a039, 21);
        a=STEPI( a, b, c, d, GET(ctx,12), 0x655b59c3, 6);
        d=STEPI( d, a, b, c, GET(ctx,3), 0x8f0ccc92, 10);
        c=STEPI( c, d, a, b, GET(ctx,10), 0xffeff47d, 15);
        b=STEPI( b, c, d, a, GET(ctx,1), 0x85845dd1, 21);
        a=STEPI( a, b, c, d, GET(ctx,8), 0x6fa87e4f, 6);
        d=STEPI( d, a, b, c, GET(ctx,15), 0xfe2ce6e0, 10);
        c=STEPI( c, d, a, b, GET(ctx,6), 0xa3014314, 15);
        b=STEPI( b, c, d, a, GET(ctx,13), 0x4e0811a1, 21);
        a=STEPI( a, b, c, d, GET(ctx,4), 0xf7537e82, 6);
        d=STEPI( d, a, b, c, GET(ctx,11), 0xbd3af235, 10);
        c=STEPI( c, d, a, b, GET(ctx,2), 0x2ad7d2bb, 15);
        b=STEPI( b, c, d, a, GET(ctx,9), 0xeb86d391, 21);

        a += saved_a;
        b += saved_b;
        c += saved_c;
        d += saved_d;

        ptr += 64;
        } while (size -= 64);

        ctx->a = a;
        ctx->b = b;
        ctx->c = c;
        ctx->d = d;

        return ptr;
        }

        void MD5_Init(struct MD5_CTX *ctx)
        {
        ctx->a = 0x67452301;
        ctx->b = 0xefcdab89;
        ctx->c = 0x98badcfe;
        ctx->d = 0x10325476;

        ctx->lo = 0;
        ctx->hi = 0;
        }
        void MD5_Update(struct MD5_CTX *ctx, void *data, unsigned long size)
        {
        unsigned int saved_lo;
        unsigned long used;
        unsigned long available;

        saved_lo = ctx->lo;
        if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo)
        ctx->hi++;
        ctx->hi += size >> 29;

        used = saved_lo & 0x3f;

        if (used) {
        available = 64 - used;

        if (size < available) {
        memcpy(&ctx->buffer[used], data, size);
        return;
        }

        memcpy(&ctx->buffer[used], data, available);
        data = (unsigned char *)data + available;
        size -= available;
        body(ctx, ctx->buffer, 64);
        }

        if (size >= 64) {
        data = body(ctx, data, size & ~(unsigned long)0x3f);
        size &= 0x3f;
        }

        memcpy(ctx->buffer, data, size);
        }
        void MD5_Final(unsigned char *result, struct MD5_CTX *ctx)
        {
        unsigned long used;
        unsigned long available;

        used = ctx->lo & 0x3f;

        ctx->buffer[used++] = 0x80;

        available = 64 - used;

        if (available < 8) {
        memset(&ctx->buffer[used], 0, available);
        body(ctx, ctx->buffer, 64);
        used = 0;
        available = 64;
        }

        memset(&ctx->buffer[used], 0, available - 8);

        ctx->lo <<= 3;
        ctx->buffer[56] = ctx->lo;
        ctx->buffer[57] = rsh(ctx->lo, 8);
        ctx->buffer[58] = rsh(ctx->lo, 16);
        ctx->buffer[59] = rsh(ctx->lo, 24);
        ctx->buffer[60] = ctx->hi;
        ctx->buffer[61] = rsh(ctx->hi, 8);
        ctx->buffer[62] = rsh(ctx->hi, 16);
        ctx->buffer[63] = rsh(ctx->hi, 24);

        body(ctx, ctx->buffer, 64);

        result[0] = ctx->a;
        result[1] = rsh(ctx->a , 8);
        result[2] = rsh(ctx->a , 16);
        result[3] = rsh(ctx->a , 24);
        result[4] = ctx->b;
        result[5] = rsh(ctx->b , 8);
        result[6] = rsh(ctx->b , 16);
        result[7] = rsh(ctx->b , 24);
        result[8] = ctx->c;
        result[9] = rsh(ctx->c , 8);
        result[10] = rsh(ctx->c , 16);
        result[11] = rsh(ctx->c , 24);
        result[12] = ctx->d;
        result[13] = rsh(ctx->d , 8);
        result[14] = rsh(ctx->d , 16);
        result[15] = rsh(ctx->d , 24);

        memset(ctx, 0, sizeof(*ctx));
        }

        // Convert 16 byte MD5 hash into printable HEX encoded string
        void MD5_CONVERT(char* iMD5, char* oMD5_A)
        {
        for (int i = 0; i < 16; i++)
        {
        sprintf(oMD5_A, "%02x", iMD5[i]);
        oMD5_A += 2;
        }
        *oMD5_A = 0;
        }


        // simple (meaning not foolproof) implementation to get the value of an xml element.
        char* GetStringXMLValue(char* xml, char* element, char* buffer)
        {
        char* retval = 0;
        char* v = strstrskip(xml, element);
        if (v!=0)
        {
        char* closing=index(v, '<');
        if (closing != 0)
        {
        *closing=0;
        strcpy(buffer,v);
        *closing='<';
        retval=buffer;
        }
        }
        return retval;
        }

        char* GetResponse(char* c, char* p, char* buffer)
        {
        // Struct to do MD5 calculations
        struct MD5_CTX ctx;

        char HA1_TEXT[1000]; HA1_TEXT[0] = 0;
        sprintf(HA1_TEXT, "%s-%s", c, p);

        // Convert according to UTF-16LE
        // Assume we use no non-acii characs when converting char* to UTF-16LE
        int l = strlen(HA1_TEXT);
        char* c1 = (char*)HA1_TEXT+2*l;
        char* c2 = (char*)HA1_TEXT+l;
        int i=l;

        c1[1] = 0;
        c1[2] = 0;
        while (i>=0)
        {
        c1[0] = *c2;
        c1[1] = 0;
        c1 -= 2;
        c2 -= 1;
        i--;
        }
        MD5_Init(&ctx);
        MD5_Update(&ctx, HA1_TEXT, l*2);
        char HA1_MD5[16];
        MD5_Final((unsigned char*)HA1_MD5, &ctx);
        char HA1_MD5_A[32 + 1];
        MD5_CONVERT((unsigned char*)HA1_MD5, HA1_MD5_A);
        sprintf(buffer, "%s-%s", c, HA1_MD5_A);
        return buffer;
        }

        char* GetSID(char* host, char* username, char* password, char* buffer)
        {
        char* rval = 0;
        char cmd[200];cmd[0]=0;
        sprintf(cmd,"/login_sid.lua?sid=%s", buffer);
        char* r1 = httpget(host, cmd);
        if (r1!=0)
        {
        char* sid=GetStringXMLValue(r1, "<SID>", buffer);
        if (sid != 0)
        {
        if(strcmp(sid, "0000000000000000") == 0)
        {
        char c_buffer[100];
        char* c=GetStringXMLValue(r1, "<Challenge>", c_buffer);
        char r_buffer[1000]; r_buffer[0]=0;
        char* r=GetResponse(c, password, r_buffer);

        cmd[0]=0;
        sprintf(cmd, "/login_sid.lua?username=%s&response=%s",username,r) ;
        char* r2=httpget(host, cmd);
        if (r2!=0)
        {
        char* sid2=GetStringXMLValue(r2, "<SID>", buffer);
        if (sid2 != 0 && strcmp(sid2, "0000000000000000") != 0)
        {
        rval=sid2;
        }
        free(r2);
        }
        }
        else
        {
        //printf("SID already valid\n");
        rval=sid;
        }
        }
        free(r1);
        }
        if (rval == 0)
        buffer[0]=0;
        else
        printf("SID voor FritizBox is=%s\n", rval);
        return rval;
        }

        void Switch(char* host, char* ain, char* sid, float i)
        {
        char cmd[200];
        if (i > 0)
        sprintf(cmd, "/webservices/homeautoswitch.lua?ain=%s&switchcmd=setswitchon&si d=%s", ain, sid);
        else
        sprintf(cmd, "/webservices/homeautoswitch.lua?ain=%s&switchcmd=setswitchoff&s id=%s", ain, sid);
        char* res = httpget(host, cmd);
        if (res != 0)
        {
        //printf("res=%s\n", res);
        free(res);
        }
        }


        int GetSwitchState(char* host, char* ain, char* sid)
        {
        char cmd[200];
        char status;

        sprintf(cmd, "/webservices/homeautoswitch.lua?ain=%s&switchcmd=getswitchstate &sid=%s", ain, sid);
        char* res = httpget(host, cmd);
        if (res != 0)
        {
        //printf("res=%s\n", res);
        status=res[strlen(res)-2];
        //printf("status=%c\n",status);
        free(res);
        return status=='1';
        }
        return 0;
        }

        int GetSwitchPresent(char* host, char* ain, char* sid)
        {
        char cmd[200];
        char status;

        sprintf(cmd, "/webservices/homeautoswitch.lua?ain=%s&switchcmd=getswitchprese nt&sid=%s", ain, sid);
        char* res = httpget(host, cmd);
        if (res != 0)
        {
        //printf("res=%s\n", res);
        status=res[strlen(res)-2];
        //printf("status=%c\n",status);
        free(res);
        return status=='1';
        }
        return 0;
        }

        char* GetHttpBody(char* resp)
        {
        while(*resp)
        {
        if(strstr(resp, "\r\n\r\n") == resp) break;
        resp++;
        }
        //printf("\n");
        if(!*resp) return NULL;
        return resp + 4;
        }


        float GetSwitchPower(char* host, char* ain, char* sid)
        {
        char cmd[200];
        char *body;
        float power = 0;

        sprintf(cmd, "/webservices/homeautoswitch.lua?ain=%s&switchcmd=getswitchpower &sid=%s", ain, sid);
        char* res = httpget(host, cmd);
        if (res != 0)
        {
        //printf("res=%s\n", res);
        body=GetHttpBody(res);
        //printf("body=%s\n",body);
        power=atof(body);
        free(res);
        //printf("Power=%f\n",power);
        return power;
        }
        return 0;
        }

        float GetSwitchEnergy(char* host, char* ain, char* sid)
        {
        char cmd[200];
        char *body;
        float power = 0;

        sprintf(cmd, "/webservices/homeautoswitch.lua?ain=%s&switchcmd=getswitchenerg y&sid=%s", ain, sid);
        char* res = httpget(host, cmd);
        if (res != 0)
        {
        //printf("res=%s\n", res);
        body=GetHttpBody(res);
        //printf("body=%s\n",body);
        power=atof(body);
        free(res);
        //printf("Energy=%f\n",power);
        return power;
        }
        return 0;
        }

        float GetTemperature(char* host, char* ain, char* sid)
        {
        char cmd[200];
        char *body;
        float power = 0;

        sprintf(cmd, "/webservices/homeautoswitch.lua?ain=%s&switchcmd=gettemperature &sid=%s", ain, sid);
        char* res = httpget(host, cmd);
        if (res != 0)
        {
        //printf("res=%s\n", res);
        body=GetHttpBody(res);
        //printf("body=%s\n",body);
        power=atof(body);
        free(res);
        //printf("Temperature=%f\n",power);
        return power;
        }
        return 0;
        }

        // Example to check if an SID is valid
        // http://192.168.178.1/login_sid.lua?sid=xxx

        // Example to get a session id
        // http://192.168.178.1/login_sid.lua?u...=&response=xxx

        // Example to get list of ain's
        // http://192.168.178.1/webservices/hom...chlist&sid=xxx

        // Switch AIN on or off, using AIN id and session id
        // http://192.168.178.1/webservices/hom...itchon&sid=xxx

        // http://192.168.178.1/webservices/hom...itchon&sid=xxx

        // Username host and password
        #define PASSWORD ""
        #define USERNAME ""
        #define HOST ""

        // AIN 1
        // AIN 2
        char* ain1="xxx";
        char* ain2="xxx";

        // Get the SID
        char sid_buffer[100]; sid_buffer[0]=0;
        char* sid = GetSID(HOST, USERNAME, PASSWORD, sid_buffer);
        unsigned int lastrefresh_sid = getcurrenttime();
        unsigned int lastrefresh_switch = 0;
        unsigned int lastrefresh_slow = 0;


        int analoginput1 = 8; //0x2; Stimmt mit Doku nicht überein. Laut Doku muesste es 0x1 sein
        int analoginput2 = 16; //0x4;
        int inputmask = analoginput1 + analoginput2;

        while (TRUE)
        {
        // Refresh the SID every 60 seconds
        unsigned int currenttime = getcurrenttime();
        if (currenttime - lastrefresh_sid > 2700) //erhoeht auf 45 Minuten
        {
        sid = GetSID(HOST, USERNAME, PASSWORD, sid_buffer);
        lastrefresh_sid=currenttime;
        }
        int nEvents = getinputevent();
        if (nEvents) printf("nEvents=%d\n", nEvents);

        // printf("nEvents=%d\n", nEvents);
        // First process the actual event to improve responsiveness
        if (nEvents & analoginput1)
        {
        Switch(HOST, ain1, sid, getinput(0));
        printf("ain1 %d\n", getinput(0));
        }
        if (nEvents & analoginput2)
        {
        Switch(HOST, ain2, sid, getinput(1));
        printf("ain2 %d\n", getinput(1));
        }
        if (currenttime - lastrefresh_switch > 5) // every 5 seconds
        {
        int state=GetSwitchState(HOST, ain1, sid);
        setoutput(0,state);
        if (state) // wenn eingeschalten frage Power ab
        setoutput(2,GetSwitchPower(HOST, ain1, sid)/1000.0);
        else
        setoutput(2,0);
        lastrefresh_switch=currenttime;
        }

        if (currenttime - lastrefresh_slow > 60) // every 60 seconds
        {
        setoutput(1,GetSwitchPresent(HOST, ain1, sid));
        setoutput(3,GetSwitchEnergy(HOST, ain1, sid)/1000.0);
        setoutput(4,GetTemperature(HOST, ain1, sid)/10.0);
        lastrefresh_slow=currenttime;
        }

        sleep(1000);
        }

        Kommentar

        • Gast

          #5
          Good afternoon, Guido.

          First of all thank you for the code you have left, it is a very interesting contribution in relation to the functions in PicoC and md5.

          On the other hand, tell you that I tried to use the transformation code to md5 and when compiling it, I get the following error in the line:

          MD5_Final ((unsigned char *) HA1_MD5, &amp; ctx);
          "can't assign unsigned char * from char [16]"

          please, could you indicate if there is an update of this code?

          Thank you very much in advance for your help and congratulations for your contributions!

          Kommentar

          • guido4096
            Dumb Home'r
            • 11.10.2015
            • 18

            #6
            Yep, there is an update. I’ll post it this evening.

            Guido

            Kommentar

            • guido4096
              Dumb Home'r
              • 11.10.2015
              • 18

              #7
              // debug level
              int DEBUG_LEVEL=0;

              // MD5 calculations
              unsigned int F(unsigned int x, unsigned int y, unsigned int z)
              {
              return (z ^ (x & (y ^ z)));
              }
              unsigned int G(unsigned int x, unsigned int y, unsigned int z)
              {
              return (y ^ (z & (x ^ y)));
              }
              unsigned int H(unsigned int x, unsigned int y, unsigned int z)
              {
              return ((x ^ y) ^ z);
              }
              unsigned int H2(unsigned int x, unsigned int y, unsigned int z)
              {
              return (x ^ (y ^ z));
              }
              unsigned int I(unsigned int x, unsigned int y, unsigned int z)
              {
              return (y ^ (x | ~z));
              }
              unsigned int rsh(unsigned int a, unsigned int s)
              {
              return a >> s;
              // rsh operator is broken on loxone picoc on version 7.1.x and before.
              // It pulls in 1's instead of 0's. Use this code to workaround.
              //unsigned int r = a >> 1;
              //r = r & 0x7fffffff;
              //return r >> (s - 1);
              }

              unsigned int STEPF(unsigned int a, unsigned int b, unsigned int c, unsigned int d, unsigned int x, unsigned int t, unsigned int s)
              {
              a += F(b, c, d) + x + t;
              a = ((a << s) | rsh(a & 0xffffffff, (32 - s)));
              a += b;
              return a;
              }
              unsigned int STEPG(unsigned int a, unsigned int b, unsigned int c, unsigned int d, unsigned int x, unsigned int t, unsigned int s)
              {
              a += G(b, c, d) + x + t;
              a = ((a << s) | rsh(a & 0xffffffff, (32 - s)));
              a += b;
              return a;
              }
              unsigned int STEPH(unsigned int a, unsigned int b, unsigned int c, unsigned int d, unsigned int x, unsigned int t, unsigned int s)
              {
              a += H(b, c, d) + x + t;
              a = ((a << s) | rsh(a & 0xffffffff, (32 - s)));
              a += b;
              return a;
              }
              unsigned int STEPH2(unsigned int a, unsigned int b, unsigned int c, unsigned int d, unsigned int x, unsigned int t, unsigned int s)
              {
              a += H2(b, c, d) + x + t;
              a = ((a << s) | rsh(a & 0xffffffff, (32 - s)));
              a += b;
              return a;
              }
              unsigned int STEPI(unsigned int a, unsigned int b, unsigned int c, unsigned int d, unsigned int x, unsigned int t, unsigned int s)
              {
              a += I(b, c, d) + x + t;
              a = ((a << s) | rsh(a & 0xffffffff, (32 - s)));
              a += b;
              return a;
              }
              struct MD5_CTX {
              unsigned int lo;
              unsigned int hi;
              unsigned int a;
              unsigned int b;
              unsigned int c;
              unsigned int d;
              unsigned char buffer[64];
              unsigned int block[16];
              };
              unsigned int GET(struct MD5_CTX* ctx, int n)
              {
              return ctx->block[n];
              }
              unsigned int SET(struct MD5_CTX* ctx, unsigned char *ptr, int n)
              {
              ctx->block[n] = (unsigned int)ptr[n * 4] |
              ((unsigned int)ptr[n * 4 + 1] << 8) |
              ((unsigned int)ptr[n * 4 + 2] << 16) |
              ((unsigned int)ptr[n * 4 + 3] << 24) ;
              return ctx->block[n];
              }
              void *body(struct MD5_CTX *ctx, void *data, unsigned long size)
              {
              unsigned char *ptr;
              unsigned int a;
              unsigned int b;
              unsigned int c;
              unsigned int d;
              unsigned int saved_a;
              unsigned int saved_b;
              unsigned int saved_c;
              unsigned int saved_d;

              ptr = (unsigned char *)data;

              a = ctx->a;
              b = ctx->b;
              c = ctx->c;
              d = ctx->d;

              do {
              saved_a = a;
              saved_b = b;
              saved_c = c;
              saved_d = d;

              // Round 1
              a=STEPF( a, b, c, d, SET(ctx,ptr,0), 0xd76aa478, 7);
              d=STEPF( d, a, b, c, SET(ctx,ptr,1), 0xe8c7b756, 12);
              c=STEPF( c, d, a, b, SET(ctx,ptr,2), 0x242070db, 17);
              b=STEPF( b, c, d, a, SET(ctx,ptr,3), 0xc1bdceee, 22);
              a=STEPF( a, b, c, d, SET(ctx,ptr,4), 0xf57c0faf, 7);
              d=STEPF( d, a, b, c, SET(ctx,ptr,5), 0x4787c62a, 12);
              c=STEPF( c, d, a, b, SET(ctx,ptr,6), 0xa8304613, 17);
              b=STEPF( b, c, d, a, SET(ctx,ptr,7), 0xfd469501, 22);
              a=STEPF( a, b, c, d, SET(ctx,ptr,8), 0x698098d8, 7);
              d=STEPF( d, a, b, c, SET(ctx,ptr,9), 0x8b44f7af, 12);
              c=STEPF( c, d, a, b, SET(ctx,ptr,10), 0xffff5bb1, 17);
              b=STEPF( b, c, d, a, SET(ctx,ptr,11), 0x895cd7be, 22);
              a=STEPF( a, b, c, d, SET(ctx,ptr,12), 0x6b901122, 7);
              d=STEPF( d, a, b, c, SET(ctx,ptr,13), 0xfd987193, 12);
              c=STEPF( c, d, a, b, SET(ctx,ptr,14), 0xa679438e, 17);
              b=STEPF( b, c, d, a, SET(ctx,ptr,15), 0x49b40821, 22);


              // Round 2
              a=STEPG( a, b, c, d, GET(ctx,1), 0xf61e2562, 5);
              d=STEPG( d, a, b, c, GET(ctx,6), 0xc040b340, 9);
              c=STEPG( c, d, a, b, GET(ctx,11), 0x265e5a51, 14);
              b=STEPG( b, c, d, a, GET(ctx,0), 0xe9b6c7aa, 20);
              a=STEPG( a, b, c, d, GET(ctx,5), 0xd62f105d, 5);
              d=STEPG( d, a, b, c, GET(ctx,10), 0x02441453, 9);
              c=STEPG( c, d, a, b, GET(ctx,15), 0xd8a1e681, 14);
              b=STEPG( b, c, d, a, GET(ctx,4), 0xe7d3fbc8, 20);
              a=STEPG( a, b, c, d, GET(ctx,9), 0x21e1cde6, 5);
              d=STEPG( d, a, b, c, GET(ctx,14), 0xc33707d6, 9);
              c=STEPG( c, d, a, b, GET(ctx,3), 0xf4d50d87, 14);
              b=STEPG( b, c, d, a, GET(ctx,8), 0x455a14ed, 20);
              a=STEPG( a, b, c, d, GET(ctx,13), 0xa9e3e905, 5);
              d=STEPG( d, a, b, c, GET(ctx,2), 0xfcefa3f8, 9);
              c=STEPG( c, d, a, b, GET(ctx,7), 0x676f02d9, 14);
              b=STEPG( b, c, d, a, GET(ctx,12), 0x8d2a4c8a, 20);

              // Round 3
              a=STEPH( a, b, c, d, GET(ctx,5), 0xfffa3942, 4);
              d=STEPH2( d, a, b, c, GET(ctx,8), 0x8771f681, 11);
              c=STEPH( c, d, a, b, GET(ctx,11), 0x6d9d6122, 16);
              b=STEPH2( b, c, d, a, GET(ctx,14), 0xfde5380c, 23);
              a=STEPH( a, b, c, d, GET(ctx,1), 0xa4beea44, 4);
              d=STEPH2( d, a, b, c, GET(ctx,4), 0x4bdecfa9, 11);
              c=STEPH( c, d, a, b, GET(ctx,7), 0xf6bb4b60, 16);
              b=STEPH2( b, c, d, a, GET(ctx,10), 0xbebfbc70, 23);
              a=STEPH( a, b, c, d, GET(ctx,13), 0x289b7ec6, 4);
              d=STEPH2( d, a, b, c, GET(ctx,0), 0xeaa127fa, 11);
              c=STEPH( c, d, a, b, GET(ctx,3), 0xd4ef3085, 16);
              b=STEPH2( b, c, d, a, GET(ctx,6), 0x04881d05, 23);
              a=STEPH( a, b, c, d, GET(ctx,9), 0xd9d4d039, 4);
              d=STEPH2( d, a, b, c, GET(ctx,12), 0xe6db99e5, 11);
              c=STEPH( c, d, a, b, GET(ctx,15), 0x1fa27cf8, 16);
              b=STEPH2( b, c, d, a, GET(ctx,2), 0xc4ac5665, 23);

              // Round 4
              a=STEPI( a, b, c, d, GET(ctx,0), 0xf4292244, 6);
              d=STEPI( d, a, b, c, GET(ctx,7), 0x432aff97, 10);
              c=STEPI( c, d, a, b, GET(ctx,14), 0xab9423a7, 15);
              b=STEPI( b, c, d, a, GET(ctx,5), 0xfc93a039, 21);
              a=STEPI( a, b, c, d, GET(ctx,12), 0x655b59c3, 6);
              d=STEPI( d, a, b, c, GET(ctx,3), 0x8f0ccc92, 10);
              c=STEPI( c, d, a, b, GET(ctx,10), 0xffeff47d, 15);
              b=STEPI( b, c, d, a, GET(ctx,1), 0x85845dd1, 21);
              a=STEPI( a, b, c, d, GET(ctx,8), 0x6fa87e4f, 6);
              d=STEPI( d, a, b, c, GET(ctx,15), 0xfe2ce6e0, 10);
              c=STEPI( c, d, a, b, GET(ctx,6), 0xa3014314, 15);
              b=STEPI( b, c, d, a, GET(ctx,13), 0x4e0811a1, 21);
              a=STEPI( a, b, c, d, GET(ctx,4), 0xf7537e82, 6);
              d=STEPI( d, a, b, c, GET(ctx,11), 0xbd3af235, 10);
              c=STEPI( c, d, a, b, GET(ctx,2), 0x2ad7d2bb, 15);
              b=STEPI( b, c, d, a, GET(ctx,9), 0xeb86d391, 21);

              a += saved_a;
              b += saved_b;
              c += saved_c;
              d += saved_d;

              ptr += 64;
              } while (size -= 64);

              ctx->a = a;
              ctx->b = b;
              ctx->c = c;
              ctx->d = d;

              return ptr;
              }

              void MD5_Init(struct MD5_CTX *ctx)
              {
              ctx->a = 0x67452301;
              ctx->b = 0xefcdab89;
              ctx->c = 0x98badcfe;
              ctx->d = 0x10325476;

              ctx->lo = 0;
              ctx->hi = 0;
              }
              void MD5_Update(struct MD5_CTX *ctx, void *data, unsigned long size)
              {
              unsigned int saved_lo;
              unsigned long used;
              unsigned long available;

              saved_lo = ctx->lo;
              if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo)
              ctx->hi++;
              ctx->hi += size >> 29;

              used = saved_lo & 0x3f;

              if (used) {
              available = 64 - used;

              if (size < available) {
              memcpy(&ctx->buffer[used], data, size);
              return;
              }

              memcpy(&ctx->buffer[used], data, available);
              data = (unsigned char *)data + available;
              size -= available;
              body(ctx, ctx->buffer, 64);
              }

              if (size >= 64) {
              data = body(ctx, data, size & ~(unsigned long)0x3f);
              size &= 0x3f;
              }

              memcpy(ctx->buffer, data, size);
              }
              void MD5_Final(char *result, struct MD5_CTX *ctx)
              {
              unsigned long used;
              unsigned long available;

              used = ctx->lo & 0x3f;

              ctx->buffer[used++] = 0x80;

              available = 64 - used;

              if (available < 8) {
              memset(&ctx->buffer[used], 0, available);
              body(ctx, ctx->buffer, 64);
              used = 0;
              available = 64;
              }

              memset(&ctx->buffer[used], 0, available - 8);

              ctx->lo <<= 3;
              ctx->buffer[56] = ctx->lo;
              ctx->buffer[57] = rsh(ctx->lo, 8);
              ctx->buffer[58] = rsh(ctx->lo, 16);
              ctx->buffer[59] = rsh(ctx->lo, 24);
              ctx->buffer[60] = ctx->hi;
              ctx->buffer[61] = rsh(ctx->hi, 8);
              ctx->buffer[62] = rsh(ctx->hi, 16);
              ctx->buffer[63] = rsh(ctx->hi, 24);

              body(ctx, ctx->buffer, 64);

              result[0] = ctx->a;
              result[1] = rsh(ctx->a , 8);
              result[2] = rsh(ctx->a , 16);
              result[3] = rsh(ctx->a , 24);
              result[4] = ctx->b;
              result[5] = rsh(ctx->b , 8);
              result[6] = rsh(ctx->b , 16);
              result[7] = rsh(ctx->b , 24);
              result[8] = ctx->c;
              result[9] = rsh(ctx->c , 8);
              result[10] = rsh(ctx->c , 16);
              result[11] = rsh(ctx->c , 24);
              result[12] = ctx->d;
              result[13] = rsh(ctx->d , 8);
              result[14] = rsh(ctx->d , 16);
              result[15] = rsh(ctx->d , 24);

              memset(ctx, 0, sizeof(*ctx));
              }

              // Convert 16 byte MD5 hash into printable HEX encoded string
              void MD5_CONVERT(char* iMD5, char* oMD5_A)
              {
              for (int i = 0; i < 16; i++)
              {
              sprintf(oMD5_A, "%02x", iMD5[i]);
              oMD5_A += 2;
              }
              *oMD5_A = 0;
              }


              // simple (meaning not foolproof) implementation to get the value of an xml element.
              char* GetStringXMLValue(char* xml, char* element, char* buffer)
              {
              char* retval = 0;
              char* v = strstrskip(xml, element);
              if (v!=0)
              {
              char* closing=index(v, '<');
              if (closing != 0)
              {
              *closing=0;
              strcpy(buffer,v);
              *closing='<';
              retval=buffer;
              }
              }
              return retval;
              }

              char* GetResponse(char* c, char* p, char* buffer)
              {
              // Struct to do MD5 calculations
              struct MD5_CTX ctx;

              char HA1_TEXT[1000]; HA1_TEXT[0] = 0;
              sprintf(HA1_TEXT, "%s-%s", c, p);

              // Convert according to UTF-16LE
              // Assume we use no non-acii characs when converting char* to UTF-16LE
              int l = strlen(HA1_TEXT);
              char* c1 = (char*)HA1_TEXT+2*l;
              char* c2 = (char*)HA1_TEXT+l;
              int i=l;

              c1[1] = 0;
              c1[2] = 0;
              while (i>=0)
              {
              c1[0] = *c2;
              c1[1] = 0;
              c1 -= 2;
              c2 -= 1;
              i--;
              }
              MD5_Init(&ctx);
              MD5_Update(&ctx, HA1_TEXT, l*2);
              char* HA1_MD5=(char*)malloc(16);
              MD5_Final(HA1_MD5, &ctx);
              char* HA1_MD5_A=(char*)malloc(32 + 1);
              MD5_CONVERT(HA1_MD5, HA1_MD5_A);
              sprintf(buffer, "%s-%s", c, HA1_MD5_A);
              free(HA1_MD5);
              free(HA1_MD5_A);
              return buffer;
              }

              char* GetSID(char* host, char* username, char* password, char* buffer)
              {
              char* rval = 0;
              char cmd[200];cmd[0]=0;
              sprintf(cmd,"/login_sid.lua?sid=%s", buffer);
              char* r1 = httpget(host, cmd);
              if (r1!=0)
              {
              char* sid=GetStringXMLValue(r1, "<SID>", buffer);
              if (sid != 0)
              {
              if(strcmp(sid, "0000000000000000") == 0)
              {
              char c_buffer[100];
              char* c=GetStringXMLValue(r1, "<Challenge>", c_buffer);
              char r_buffer[1000]; r_buffer[0]=0;
              char* r=GetResponse(c, password, r_buffer);

              cmd[0]=0;
              sprintf(cmd, "/login_sid.lua?username=%s&response=%s",username,r) ;
              char* r2=httpget(host, cmd);
              if (r2!=0)
              {
              char* sid2=GetStringXMLValue(r2, "<SID>", buffer);
              if (sid2 != 0 && strcmp(sid2, "0000000000000000") != 0)
              {
              rval=sid2;
              }
              free(r2);
              }
              }
              else
              {
              //printf("SID already valid\n");
              rval=sid;
              }
              }
              free(r1);
              }
              if (rval == 0)
              buffer[0]=0;
              else
              if (DEBUG_LEVEL>0) printf("SID voor FritizBox is=%s\n", rval);
              return rval;
              }

              void Switch(char* host, char* ain, char* sid, float i)
              {
              char cmd[200];
              if (i > 0)
              sprintf(cmd, "/webservices/homeautoswitch.lua?ain=%s&switchcmd=setswitchon&si d=%s", ain, sid);
              else
              sprintf(cmd, "/webservices/homeautoswitch.lua?ain=%s&switchcmd=setswitchoff&s id=%s", ain, sid);
              char* res = httpget(host, cmd);
              if (res != 0)
              {
              if (DEBUG_LEVEL>0) printf("res=%s\n", res);
              free(res);
              }
              }

              // Example to check if an SID is valid
              // http://192.168.178.1/login_sid.lua?sid=340c8a189ca5443c

              // Example to get a session id
              // http://192.168.178.1/login_sid.lua?u...6dfeada1dcba44

              // Example to get list of ain's
              // http://192.168.178.1/webservices/hom...a744e1232755fc

              // Switch AIN on or off, using AIN id and session id
              // http://192.168.178.1/webservices/hom...a744e1232755fc
              // http://192.168.178.1/webservices/hom...a744e1232755fc

              // http://192.168.178.1/webservices/hom...a744e1232755fc
              // http://192.168.178.1/webservices/hom...a744e1232755fc


              // Username host and password
              #define PASSWORD "xxx"
              #define USERNAME ""
              #define HOST "192.168.178.1"

              // AIN 1
              // AIN 2
              char* ain1="xxx";
              char* ain2="yyy";
              int refreshtime=60;

              // Get the SID
              char sid_buffer[100]; sid_buffer[0]=0;
              char* sid = GetSID(HOST, USERNAME, PASSWORD, sid_buffer);
              unsigned int lastrefresh_sid = getcurrenttime();


              int analoginput1 = 0x2;
              int analoginput2 = 0x4;
              int analoginput3 = 0x6;

              while (TRUE)
              {
              // Refresh the SID every 60 seconds
              unsigned int currenttime = getcurrenttime();
              if (currenttime - lastrefresh_sid > refreshtime)
              {
              sid = GetSID(HOST, USERNAME, PASSWORD, sid_buffer);
              lastrefresh_sid=currenttime;
              }
              int nEvents = getinputevent();
              if ( (nEvents & 0xe) )
              {
              //printf("e=%d\n", nEvents);
              // First process the actual event to improve responsiveness
              if (nEvents & analoginput1)
              {
              Switch(HOST, ain1, sid, getinput(0));
              //printf("ain1 %d\n", getinput(0));
              }
              if (nEvents & analoginput2)
              {
              Switch(HOST, ain2, sid, getinput(1));
              //printf("ain2 %d\n", getinput(1));
              }

              }
              sleep(30);
              }

              Kommentar

              • Gast

                #8
                Hello Guido.
                First of all, thank you for sending the code update, now it does not give compilation errors.

                I have tried to make a modification to only perform the calculation of the checksum md5 only of 1 actuator, and using the string "abc". The result offered by the code "ffffff7bffff3a22ffff00ff3c5e1b78" differs from the real md5 "900150983cd24fb0d6963f7d28e17f72".

                Please, could you tell me what is the mistake I may be making wrong?

                Thank you very much for your help!!!!


                Regards

                Kommentar

                • svethi
                  Lebende Foren Legende
                  • 25.08.2015
                  • 6320

                  #9
                  Das geht auch alles einfacher wenn man einen Loxberry mit dem Fritz.Lox Plugin benutzt
                  Miniserver; KNX; Vitogate; EnOcean (EnOceanPi); Loxone Air; Caldav-Kalenderanbindung; RaspberryPi und für keine Frickellösung zu schade :-)

                  Kommentar

                  • guido4096
                    Dumb Home'r
                    • 11.10.2015
                    • 18

                    #10
                    Gast you’ll need to tell in more detail want you’r doing. The mdf5 calculation is working well for me. Requires an up to date Loxone, old versions have a bug in the rsh operator
                    svethi I did not know Aboutaleb loxberry. Good to know. Of course you need another component, not needed for me at the moment, but if I want to do more than indeed this could be useful
                    Zuletzt geändert von guido4096; 24.02.2018, 11:54. Grund: typo

                    Kommentar

                    • Gast

                      #11
                      svethi, thank you very much for your contribution. I had heard about Loxberry and if I need it, I will study it to incorporate it into my system.

                      Guido, I've made sure that I have the latest version of the loxone server.
                      On the other hand, reviewing the getResponse function, I have made a modification so that it only makes the translation to md5 of a text string.

                      char * GetResponse (char * c, char * buffer)
                      {
                      // Struct to do MD5 calculations
                      struct MD5_CTX ctx;

                      char HA1_TEXT [1000]; HA1_TEXT [0] = 0;
                      sprintf (HA1_TEXT, "% s", c);

                      // Convert according to UTF-16LE
                      // Assume we use non-acii characs when converting char * to UTF-16LE
                      int l = strlen (HA1_TEXT);
                      char * c1 = (char *) HA1_TEXT + 2 * l;
                      char * c2 = (char *) HA1_TEXT + l;
                      int i = l;

                      c1 [1] = 0;
                      c1 [2] = 0;
                      while (i> = 0)
                      {
                      c1 [0] = * c2;
                      c1 [1] = 0;
                      c1 - = 2;
                      c2 - = 1;
                      i--;
                      }
                      MD5_Init (& ctx);
                      MD5_Update (& ctx, HA1_TEXT, l * 2);
                      char * HA1_MD5 = (char *) malloc (16);
                      MD5_Final (HA1_MD5, & ctx);
                      char * HA1_MD5_A = (char *) malloc (32 + 1);
                      MD5_CONVERT (HA1_MD5, HA1_MD5_A);
                      sprintf (buffer, "% s", HA1_MD5_A);

                      printf ("Md5:% s-% s", buffer, c);
                      free (HA1_MD5);
                      free (HA1_MD5_A);
                      return buffer;
                      }

                      char * c = "abc";
                      char r_buffer [1000]; r_buffer [0] = 0;
                      char * r = GetResponse (c, r_buffer);

                      The problem that arises is that the execution of the function when entering "abc" as input, gives "ffffff7bffff3a22ffff00ff3c5e1b78" as result, verydifferent from the real md5 "900150983cd24fb0d6963f7d28e17f72".

                      What am I doing wrong?


                      Thank you very much for your help!!!!


                      Regards

                      Kommentar

                      • guido4096
                        Dumb Home'r
                        • 11.10.2015
                        • 18

                        #12
                        Likely because my routine is converting first to utf16, by appending a 0 byte after each character. This means each character is two bytes. When you do the conversion, you do it in utf8, which is only one char per byte for regular ascii characters. UTF16 is required for fritz box.
                        Zuletzt geändert von guido4096; 25.02.2018, 21:08.

                        Kommentar

                        Lädt...