Gurglee Premium
Manda le misure a Gurglee dal tuo impianto
Se hai già una sonda che legge la temperatura o il pH, non c'è motivo di ribattere quei numeri a mano. Gurglee ti dà un indirizzo e una chiave: quello che sa fare una chiamata HTTP può scriverci dentro.
Da leggere prima di tutto il resto: non è un'integrazione con SmartLife o Tuya. Quei servizi vogliono un contratto commerciale per l'uso in produzione, e per andare noi a leggere i tuoi sensori dovremmo custodire le tue credenziali di quei cloud — una responsabilità che non vogliamo prenderci per una funzione da pochi euro al mese.
Quello che c'è, invece, funziona con qualunque sistema: Home Assistant, Node-RED, ESPHome, un controller da acquario, uno script su un Raspberry, perfino un'automazione che parte dall'app SmartLife. Il pezzo che collega il tuo sensore a Gurglee lo scrivi tu — sotto ci sono gli esempi già pronti, ed è questione di cinque righe.
1 · Come ottenere la chiave
Serve il piano Premium e un account Gurglee (le misure arrivano al tuo profilo, e da lì scendono su tutti i tuoi dispositivi).
- Nell'app, Menu → Strumenti → «API e integrazioni».
- Accendi l'interruttore API attiva.
- Tocca Crea una chiave e dalle un nome che ti ricordi dove finirà («Home Assistant», «ESP sala»).
- Copiala subito. La vedi una volta sola: sul server ne resta solo un'impronta, quindi non possiamo rimostrartela. Se la perdi, la revochi e ne fai un'altra.
La chiave comincia per glk_ e va nell'header Authorization, come un normale bearer token. Puoi averne fino a 5 attive insieme: una per sistema, così ne revochi una senza spegnere le altre.
Nella stessa schermata c'è il bottone «Prova la chiamata»: manda una richiesta vera con la tua chiave vera, ma non salva niente. È il modo più rapido per scoprire che hai sbagliato il nome della vasca, invece di guardare i log del tuo Home Assistant.
2 · Gli endpoint
Base: https://cyykgbgeswcflhxjgsag.supabase.co/functions/v1/api/v1
GET /tanks — cosa posso scrivere
La chiamata da fare per prima: dice come si chiamano le tue vasche, quali parametri seguono, con che unità, e i limiti in vigore.
curl -H "Authorization: Bearer glk_LA_TUA_CHIAVE" \
https://cyykgbgeswcflhxjgsag.supabase.co/functions/v1/api/v1/tanks
{
"ok": true,
"tanks": [
{
"id": "0f0b…",
"name": "Reef",
"type": "reef",
"volumeLiters": 300,
"parameters": [
{ "key": "temperature", "unit": "°C", "accepts": ["°C", "°F", "K"] },
{ "key": "ph", "unit": "pH", "accepts": ["pH"] }
]
}
],
"limits": { "requestsPerDay": 500, "minIntervalMinutes": { "temperature": 60, "default": 1440 } }
}
POST /readings — mandare una misura
| Campo | Obbligatorio | Cos'è |
|---|---|---|
tank | sì | L'id, il nome o un alias della vasca. Il nome è più comodo; se due vasche si chiamano uguale la richiesta viene respinta con l'elenco degli id, invece di indovinare. |
parameter | sì | La chiave del parametro (temperature, ph, kh…): l'elenco è nella tabella qui sotto. Valgono anche i parametri personalizzati che hai creato tu. |
value | sì | Il numero. Si accetta anche come stringa e con la virgola decimale ("24,6"), perché è quello che produce un template con la lingua italiana. |
unit | no | Se manca si assume l'unità canonica del parametro. Le forme scritte sono generose: C, celsius, °C, mg/L, uS/cm. |
measuredAt | no | Quando è stata presa, in ISO 8601 (2026-08-11T18:30:00Z) o come epoch. Se manca, adesso. |
note | no | Fino a 500 caratteri, come nel form dell'app. |
dryRun | no | true controlla tutto e non salva nulla. È quello che usa il bottone di prova. |
La risposta di una scrittura andata a buon fine è un 201:
{
"ok": true,
"message": "Reading stored. It will show up in the app at the next sync.",
"reading": {
"id": "…", "tank": { "id": "0f0b…", "name": "Reef" },
"parameter": "temperature", "value": 77, "unit": "°F",
"valueCanonical": 25, "canonicalUnit": "°C",
"measuredAt": "2026-08-11T18:30:00.000Z", "minIntervalMinutes": 60
}
}
Come vedi il valore viene convertito nell'unità canonica del parametro, esattamente come quando lo digiti nell'app: manda pure i gradi Fahrenheit se il tuo sensore parla così.
3 · Parametri e unità
| Chiave | Canonica | Unità accettate |
|---|---|---|
temperature | °C | °C, °F, K |
ph | pH | pH |
ammonia, nitrite, nitrate | ppm | ppm (= mg/L) |
kh | dKH | dKH, meq/L, ppm |
gh | dGH | dGH, ppm, meq/L |
tds | ppm | ppm, µS/cm, mS/cm |
salinity | ppt | ppt, SG, mS/cm |
orp | mV | mV |
calcium, magnesium, phosphate, co2, iron, potassium, silicate, copper, oxygen | ppm | ppm |
Un parametro personalizzato creato nell'app funziona con la sua chiave e la sua unità: conversioni non ne conosciamo, quindi accetta solo quella.
Non serve che la vasca segua già quel parametro perché la misura venga accettata — ma per vederla nei grafici e nella panoramica conviene aggiungerlo ai parametri seguiti dell'acquario.
4 · Ogni quanto si può mandare
| Parametro | Una misura ogni |
|---|---|
temperature | 60 minuti |
| tutti gli altri | 24 ore |
Non è avarizia di spazio: un valore ogni dieci secondi non aggiunge informazione, riempie lo storico e rende i grafici illeggibili. La temperatura fa eccezione perché è l'unico valore che una sonda misura davvero in continuo e di cui interessa l'andamento nella giornata.
Se mandi prima del tempo ricevi un 429 con scritto quanti secondi mancano (anche nell'header Retry-After): la tua automazione può semplicemente riprovare al giro dopo. Il conteggio guarda solo le misure arrivate dall'API: quello che inserisci a mano nell'app non blocca il sensore.
C'è anche un tetto di 500 richieste al giorno per chiave, valide o sbagliate che siano. Serve a fermare un ciclo impazzito, non a limitare l'uso normale: con i tempi qui sopra, una vasca ben strumentata ne consuma qualche decina.
5 · Esempi pronti
Terminale (curl)
curl -X POST https://cyykgbgeswcflhxjgsag.supabase.co/functions/v1/api/v1/readings \
-H "Authorization: Bearer glk_LA_TUA_CHIAVE" \
-H "Content-Type: application/json" \
-d '{"tank": "Reef", "parameter": "temperature", "value": 25.4, "unit": "°C"}'
Home Assistant
In configuration.yaml, un rest_command più un'automazione che lo chiama ogni ora:
rest_command:
gurglee_temperatura:
url: "https://cyykgbgeswcflhxjgsag.supabase.co/functions/v1/api/v1/readings"
method: POST
headers:
Authorization: "Bearer glk_LA_TUA_CHIAVE"
Content-Type: "application/json"
payload: >-
{"tank": "Reef", "parameter": "temperature",
"value": {{ states("sensor.temperatura_acquario") }} }
automation:
- alias: "Gurglee — temperatura ogni ora"
trigger:
- platform: time_pattern
hours: "/1"
condition:
# Non mandare "unknown" quando il sensore non risponde: finirebbe
# rifiutato con un 400 e riempirebbe il log di errori.
- condition: template
value_template: "{{ states('sensor.temperatura_acquario') not in ['unknown', 'unavailable'] }}"
action:
- service: rest_command.gurglee_temperatura
Node-RED
Un nodo function che prepara il messaggio, seguito da un http request (POST) verso l'endpoint:
msg.headers = {
"Authorization": "Bearer glk_LA_TUA_CHIAVE",
"Content-Type": "application/json"
};
msg.payload = {
tank: "Reef",
parameter: "temperature",
value: msg.payload // il numero che arriva dal sensore
};
return msg;
Python / Raspberry Pi
import requests
requests.post(
"https://cyykgbgeswcflhxjgsag.supabase.co/functions/v1/api/v1/readings",
headers={"Authorization": "Bearer glk_LA_TUA_CHIAVE"},
json={"tank": "Reef", "parameter": "ph", "value": 8.12},
timeout=20,
)
ESPHome
Direttamente dall'ESP, senza passare da Home Assistant:
http_request:
useragent: esphome/gurglee
timeout: 10s
sensor:
- platform: dallas_temp
name: "Acquario"
id: temp_acquario
update_interval: 60min
on_value:
- http_request.post:
url: "https://cyykgbgeswcflhxjgsag.supabase.co/functions/v1/api/v1/readings"
headers:
Authorization: "Bearer glk_LA_TUA_CHIAVE"
Content-Type: "application/json"
json:
tank: "Reef"
parameter: "temperature"
value: !lambda "return id(temp_acquario).state;"
PowerShell
Invoke-RestMethod -Method Post `
-Uri "https://cyykgbgeswcflhxjgsag.supabase.co/functions/v1/api/v1/readings" `
-Headers @{ Authorization = "Bearer glk_LA_TUA_CHIAVE" } `
-ContentType "application/json" `
-Body '{"tank":"Reef","parameter":"temperature","value":25.4}'
6 · SmartLife, Tuya e simili
È la domanda che arriva per prima, quindi rispondiamo per esteso.
Perché non c'è un'integrazione diretta. Il piano gratuito delle API cloud di Tuya — quello su cui gira SmartLife e buona parte dei termometri WiFi economici, Inkbird compresi — è dichiaratamente «solo per sviluppo»: per usarlo in produzione serve un contratto commerciale, con costi fuori scala per un'app da pochi euro al mese. E anche potendolo pagare, per leggere i tuoi sensori dovremmo tenere le tue credenziali di quel cloud sui nostri server: più dati sensibili di tutto il resto di Gurglee messo insieme.
Come si colma la distanza. Serve qualcosa che stia in mezzo, e quasi sempre è già in casa:
- Home Assistant — se ce l'hai, hai già i tuoi dispositivi Tuya/SmartLife dentro (integrazione ufficiale o LocalTuya). Da lì basta il blocco della sezione 5. È la strada che consigliamo: si fa una volta e funziona per sempre, anche senza il cloud del produttore quando usi LocalTuya.
- Node-RED con i nodi
node-red-contrib-tuya-smart-device: legge il sensore in rete locale e chiama Gurglee. - Uno script con
tinytuya(Python) su un Raspberry o un PC sempre acceso, lanciato dal cron ogni ora. - L'automazione dell'app SmartLife stessa, se il tuo modello espone i «webhook» nelle scene: punta al nostro endpoint e sei a posto senza altro hardware.
- Controller da acquario (Apex, GHL, Seneye…): quasi tutti sanno fare una POST o esporre un JSON che uno script legge.
Se un giorno l'integrazione diretta diventerà sostenibile la faremo, e chi nel frattempo ha usato l'API non perderà nulla: le misure sono già dentro Gurglee, con la stessa forma.
7 · Errori
Ogni errore è un JSON con un error stabile (per il codice) e un message in inglese leggibile (per te che stai guardando i log).
| HTTP | error | Cosa fare |
|---|---|---|
| 401 | missing_token | Manca l'header Authorization. |
| 401 | invalid_token | Chiave sconosciuta o revocata. Fanne un'altra dall'app. |
| 403 | api_disabled | L'interruttore è spento: riaccendilo in «API e integrazioni». |
| 403 | premium_required | L'abbonamento collegato a quella chiave non è attivo. |
| 400 | unknown_tank · ambiguous_tank | La risposta elenca le vasche con i loro id: usa l'id. |
| 400 | unknown_parameter · bad_unit · bad_value | La risposta dice cosa è accettato per quel parametro. |
| 400 | bad_measured_at | Data illeggibile, nel futuro o più vecchia di un anno (di solito un epoch in secondi letto come millisecondi). |
| 429 | too_soon | Troppo presto per quel parametro: retryAfterSeconds dice quanto manca. |
| 429 | daily_cap | Superate le 500 richieste della giornata. Il contatore riparte a mezzanotte UTC. |
| 500 | server_error | Colpa nostra: riprova più tardi. |
8 · Sicurezza e privacy
- La chiave è un titolo al portatore: chi la legge può scrivere nelle tue vasche. Trattala come una password — niente repository pubblici, niente screenshot.
- Sul nostro server non c'è la chiave, ma la sua impronta (sha256). Non possiamo rimostrartela nemmeno volendo, ed è il motivo per cui va copiata subito.
- La chiave scrive soltanto letture: non legge il diario, non tocca le attività, non può cancellare niente. L'unica cosa che può leggere è l'elenco delle tue vasche e dei parametri che seguono (
GET /tanks), che è ciò che le serve per sapere dove scrivere. - Puoi revocarne una quando vuoi, o spegnere l'interruttore per fermarle tutte insieme senza perdere la configurazione.
- Nell'app ogni misura porta scritto se l'hai inserita tu o se è arrivata dall'API: guardando lo storico sai sempre da dove viene un numero.
Qualcosa non torna, o hai collegato un sistema che qui non è elencato e vorresti vederlo comparire? Scrivici: [email protected].