TapHome

Esempi di script

Script TapHome completi: divisione sicura, 0 invece di un valore sconosciuto, dimmerazione con LINEAR, allarmi e attributi di servizio Modbus, HTTP e JSON, listener MQTT e Wake on LAN.

Script completi per attività comuni. Le ricette per smart rule funzionano in qualsiasi installazione; gli esempi Modbus e Packet parser provengono dai repository ufficiali dei modelli per Modbus e Packet parser. I nomi brevi come Se, va1 o St sono abbreviazioni dei valori dei dispositivi scelti nell’editor degli script; nomi come StatusJson o plugTopic sono variabili del modulo.

Ricette per smart rule

Dividere due valori in modo sicuro

Dividere per un valore di dispositivo pari a 0 interrompe lo script con Line in script: 1 Message: Division by zero. Racchiudi la divisione va1/va2 nella forma a funzione di IF. Valuta solo il ramo scelto, quindi la divisione viene eseguita solo quando il divisore è maggiore di 0:

IF(va2 > 0, va1/va2, 0);
# Result: 0 when va2 is 0, otherwise va1 divided by va2

Quando il divisore può essere negativo, verifica invece va2 <> 0. Maggiori dettagli in Errori.

Scrivere 0 invece di un valore sconosciuto

Un valore di dispositivo sconosciuto, ad esempio da un sensore in errore, è NaN. Per scrivere invece 0, verificalo con ISNAN — lo schema è IF(ISNAN(x), 0, x):

IF(ISNAN(Se), 0, Se);
# Result: 0 while the value of Se is unknown, otherwise the value of Se

Un confronto come Se = NaN è un errore di parsing. Quando il valore può essere anche Null (nessun valore), verifica prima ISNULL; OR salta il lato destro quando il lato sinistro è TRUE:

IF(ISNULL(Se) OR ISNAN(Se), 0, Se);

Dimmerazione con LINEAR

LINEAR converte un intervallo di valori di ingresso in un intervallo di valori di uscita, ed è il modo abituale per calcolare un livello di dimmerazione. Il livello di un dimmer va da 0 (spento) a 1 (100 %). In una smart rule Formula con un dimmer come uscita, questo script abbassa la luce man mano che la luce diurna sale da 0 a 500 lx (Se è il sensore di luminosità):

LINEAR(Se, 0, 1, 500, 0);
# Result: 1 at 0 lx, 0.5 at 250 lx, 0 at 500 lx and more

Fuori dall’intervallo di ingresso il risultato resta all’estremo più vicino (BOUNDS, il valore predefinito). Per mantenere un livello minimo, alza l’uscita inferiore: LINEAR(Se, 0, 1, 500, 0.1) non scende mai sotto il 10 %. Dettagli in LINEAR.

Esempi Modbus

Errori del dispositivo da un registro di allarme

Uno script di lettura di un inverter Solax trasforma un registro di stato e una parola di allarme a 32 bit in errori del dispositivo:

VAR reg := MODBUSR(A, 0x09, UInt16);
IF(reg = 3, ADDERROR("Run Mode Fault"));
IF(reg = 4, ADDERROR("Run Mode Permanent Fault"));

VAR x3 := MODBUSR(A, 0x0040, Uint32);
IF(GETBIT(x3, 0) = 1, ADDERROR("TZ Protect Fault"));
IF(GETBIT(x3, 1) = 1, ADDERROR("Grid Lost Fault"));
IF(GETBIT(x3, 2) = 1, ADDERROR("Grid Volt Fault"));
IF(GETBIT(x3, 15) = 1, ADDERROR("Off-grid Overload"));

Attributo di servizio da campi di bit

Un attributo di servizio Komfovent mostra i quattro byte di un registro a 32 bit come testo separato da punti:

VAR reg := MODBUSR(H, 22, Uint32);
VAR first := GETBITS(reg, 0, 8);
VAR second := GETBITS(reg, 8, 8);
VAR third := GETBITS(reg, 16, 8);
VAR last := GETBITS(reg, 24, 8);
RETURN(last + "." + third + "." + second + "." + first);
# Result: text such as "1.2.3.4"

Azione di servizio con un parametro

I parametri di un’azione di servizio sono variabili denominate con la loro abbreviazione. Un’azione di Eastron SDM120 scrive il suo parametro ID nell’holding register 20:

MODBUSW(H, 20, Float, ID)

Decodificare un’enumerazione

Un registro di stato di Huawei SUN2000 diventa un testo leggibile con SWITCH:

VAR state := MODBUSR(H, 32089, Uint16);
SWITCH(state,
      0, "Idle: Initializing",
      1, "Idle: Detecting ISO",
    256, "Starting",
    512, "On-Grid",
    768, "Shutdown: Fault",
    "Unknown");

Esempi Packet parser

Richiesta HTTP con gestione degli errori

SENDHTTPREQUEST restituisce un oggetto httpresponse. Questo script di lettura (Forecast.solar) conserva il corpo della risposta nella variabile output per il parsing successivo e segnala una richiesta non riuscita come errore del dispositivo:

VAR response := SENDHTTPREQUEST("/estimate/" + Latitude + "/" + Longitude + "/"
    + PV_declination + "/" + PV_azimuth + "/" + PV_kWp,
    "GET", "", "Accept: application/json");

IF response.IsSuccess
    output := response.Content;
ELSE
    ADDERROR(response.StatusCode + " (" + response.ReasonPhrase + ")");
END

Tempo di attività da JSON

Un attributo di servizio di Shelly 1PM formatta il tempo di attività in secondi a partire dal JSON di stato in cache:

VAR upTimeSeconds := PARSEJSON(StatusJson, "uptime");
VAR days := FLOOR(upTimeSeconds / 86400, 1);
upTimeSeconds := MOD(upTimeSeconds, 86400);
VAR hours := FLOOR(upTimeSeconds / 3600, 1);
upTimeSeconds := MOD(upTimeSeconds, 3600);
VAR minutes := FLOOR(upTimeSeconds / 60, 1);
days + " day(s) " + hours + "h " + minutes + "m";
# Result: for 191820 seconds "2 day(s) 5h 17m"

Prezzo spot per il periodo corrente

Uno script di lettura scarica i prezzi di oggi da okte.sk:

VAR today := TOSTRING(NOW(), "yyyy-MM-dd");
VAR response := SENDHTTPREQUEST("/api/v1/dam/results?deliveryDayFrom=" + today
    + "&deliveryDayTo=" + today);

IF response.IsSuccess
    output := response.Content;
ELSE
    ADDERROR(response.StatusCode + " (" + response.ReasonPhrase + ")");
END

Un attributo di servizio sceglie poi un periodo con un filtro JSONPath:

PARSEJSON(output, "$[?(@.period == 1)].price") + " EUR"

Ciclo su un array JSON

Con TRUE come terzo argomento, PARSEJSON restituisce Null dopo l’ultimo elemento invece di fallire, il che termina il ciclo:

VAR response := SENDHTTPREQUEST("/api/taphomeapi/v1/discovery", "GET", "",
    "Authorization: TapHome " + API_token);

IF response.IsSuccess
    VAR ret := "";
    VAR i := 0;
    VAR id := PARSEJSON(response.Content, "$.devices[0].deviceId", TRUE);

    WHILE !ISNULL(id)
        ret := ret + "(" + id + ") ";
        ret := ret + PARSEJSON(response.Content, "$.devices[" + TOSTRING(i) + "].name", TRUE);
        ret := ret + " - " + PARSEJSON(response.Content, "$.devices[" + TOSTRING(i) + "].type", TRUE);
        ret := ret + "\n";
        i := i + 1;
        id := PARSEJSON(response.Content, "$.devices[" + TOSTRING(i) + "].deviceId", TRUE);
    LOOP

    RETURN(ret);
END

Validare una variabile del modulo in uno script listener

Uno script listener MQTT di Shelly verifica prima che la variabile del modulo plugTopic sia stata compilata, poi converte la potenza misurata da W a kW:

IF(INDEXOF(plugTopic, "shelly1pm-deviceid") = 0)
    ADDERROR("Set correct 'plugTopic' value in module variables. "
        + "Topic format is 'shelly1pm-<deviceid>'.");
    RETURN(-1);
END

IF(RECEIVEDMSG.TOPIC = "shellies/" + plugTopic + "/relay/0/power")
    VAR valueP := TOSTRING(RECEIVEDMSG.PAYLOAD);
    Ed := IF(ISNULL(valueP), NaN, TODOUBLE(valueP) / 1000.0);
END

Wake on LAN

Un pulsante UDP invia un magic packet. La variabile del modulo MAC contiene l’indirizzo come testo ("12 34 56 78 9a bc"); BYTECOLLECTION ignora gli spazi:

VAR magicPacket := BYTECOLLECTION("FF FF FF FF FF FF " + MAC + MAC + MAC + MAC
    + MAC + MAC + MAC + MAC + MAC + MAC + MAC + MAC + MAC + MAC + MAC + MAC);
SENDDATA(magicPacket);