Irsap integration

MQTT or IFTTT?

MQTT… they send metrics like temperature or other stuff… you can clearly see it using a simple nmap over the irsap host.

Oh sorry - as they support IFTTT, I thought you was referring to it. Good thing they use MQTT, I’ll try to inspect it with Wireshark maybe this way (and maybe learn something new :wink: )
Will report back!

While analyzing the traffic on my iPhone using apps that inspect data by faking SSL, I discovered that the application logs in through Amazon Cognito. However, I wasn’t able to find a typical link or endpoint where I could input credentials and directly retrieve a token. Despite uncovering the login process, the method to access or intercept the authentication token remains unclear.

Ok so I took some patience and cracked it open, the case has an outer shell (held down with various tabs, very hard to pull) and an inner shell with some Torx screws.
Seems like a custom board, with a Telit GS2200 Wi-Fi module attached.
I see some serial pinouts (J3, J7) but my limited knowledge can’t help much with it.

Disassembly photos





Here’s a manual about that GS2200M GS2200M S2W Adapter Command Reference Guide and MQTT

1 Like

What do you think if we start from this document ?

As we are mostly Italian, @ilvianez created a small Telegram group to work together. Feel free to join us, ask in DM!
Of course we’ll report back here any success :crossed_fingers:

1 Like

Hi everyone,

I’ve quickly put together a Python script that handles authentication via AWS Cognito using a username and password, and then makes a GraphQL request to fetch device data. The retrieved data is summarized and printed, including device firmware details, temperature and other useful information.

This is a rough initial version, but it could serve as a good starting point for anyone interested in helping us build a more complete integration.

We’re working on this collaboratively and anyone interested is welcome to join us on Telegram to discuss and contribute!

Here’s the script:

import requests
from warrant import Cognito
import json

# Configurazione con il tuo User Pool ID, Client ID e regione
USER_POOL_ID = 'eu-west-1_qU4ok6EGG'  # Sostituisci con il tuo User Pool ID
CLIENT_ID = '4eg8veup8n831ebokk4ii5uasf'  # Sostituisci con il tuo Client ID
REGION = 'eu-west-1'  # Sostituisci con la tua regione (esempio: eu-west-1)
USERNAME = 'IRSAPEMAILACCOUNT'  # Sostituisci con il tuo username
PASSWORD = 'PASSWORD'  # Sostituisci con la tua password

# Funzione per eseguire il login e ottenere l'access token
def login_with_srp(username, password):
    try:
        # Configurazione del client Cognito con User Pool ID, Client ID, username e regione
        u = Cognito(USER_POOL_ID, CLIENT_ID, username=username, user_pool_region=REGION)

        # Effettua il login utilizzando SRP
        u.authenticate(password=password)

        # Ottenere il token di accesso
        print("Login effettuato con successo.")
        return u.access_token

    except Exception as e:
        print(f"Errore durante il login: {e}")
        return None

# Funzione per eseguire la chiamata GraphQL con l'access token
def make_graphql_call(access_token):
    try:
        url = 'https://flqpp5xzjzacpfpgkloiiuqizq.appsync-api.eu-west-1.amazonaws.com/graphql'

        headers = {
            'Authorization': f'Bearer {access_token}',
            'Content-Type': 'application/json',
            'Host': 'flqpp5xzjzacpfpgkloiiuqizq.appsync-api.eu-west-1.amazonaws.com',
            'User-Agent': 'now2/2124 CFNetwork/1568.100.1 Darwin/24.0.0',
            'Connection': 'keep-alive',
            'Accept': '*/*',
            'x-amz-user-agent': 'aws-amplify/3.0.7 react-native',
            'Accept-Language': 'it-IT,it;q=0.9'
        }

        # Corpo della richiesta
        graphql_query = {
            "operationName": "GetShadow",
            "variables": {"envId": "c1d14842-c769-4701-abe3-1986df43f8bd"},
            "query": "query GetShadow($envId: ID!) {\n  getShadow(envId: $envId) {\n    envId\n    payload\n    __typename\n  }\n}\n"
        }

        # Eseguire la richiesta POST
        response = requests.post(url, headers=headers, json=graphql_query)

        if response.status_code == 200:
            return response.json()
        else:
            print(f"Errore nella richiesta: {response.status_code}")
            print(response.text)
            return None

    except Exception as e:
        print(f"Errore durante la chiamata GraphQL: {e}")
        return None

# Funzione per analizzare e stampare i dati
def analyze_and_print_data(response_data):
    # Parsing del payload JSON interno
    payload = json.loads(response_data['data']['getShadow']['payload'])

    # Formattazione dei dati
    output = f"""
ID dell’ambiente: {payload['id']}
• Client ID: {payload['clientId']}
• Timestamp: {payload['timestamp']}
• Dati del dispositivo “D-I”:
   • Seriale: {payload['state']['desired']['D-I_SRL']}
   • Firmware: {payload['state']['desired']['D-I_FWV']}
   • Ultimo aggiornamento: {payload['state']['desired']['D-I_LUP']}
   • Offset del sensore di temperatura: {payload['state']['desired']['D-I_X_temperatureSensorOffset']}
   • Indirizzo IP: {payload['state']['desired']['D-I_X_ipAddress']}
   • Valori VOC e CO2: {payload['state']['desired']['D-I_X_vocValue']}, {payload['state']['desired']['D-I_X_co2Value']}
   • Finestra aperta rilevata: {payload['state']['desired']['D-I_X_OpenWindowDetected']}
• Dati del dispositivo “DfO”:
   • Seriale: {payload['state']['desired']['DfO_SRL']}
   • Firmware: {payload['state']['desired']['DfO_FWV']}
   • Ultimo aggiornamento: {payload['state']['desired']['DfO_LUP']}
   • Offset del sensore di temperatura: {payload['state']['desired']['DfO_X_temperatureSensorOffset']}
   • Indirizzo IP: {payload['state']['desired']['DfO_X_ipAddress']}
• Dati del dispositivo “DO4”:
   • Seriale: {payload['state']['desired']['DO4_SRL']}
   • Firmware: {payload['state']['desired']['DO4_FWV']}
   • Ultimo aggiornamento: {payload['state']['desired']['DO4_LUP']}
   • Offset del sensore di temperatura: {payload['state']['desired']['DO4_X_temperatureSensorOffset']}
   • Indirizzo IP: {payload['state']['desired']['DO4_X_ipAddress']}
• Informazioni aggiuntive sui bagni:
   • Bagno Padronale: Temperatura attuale: {payload['state']['desired']['Pca_TMP']}
   • Bagno Piano Primo: Temperatura attuale: {payload['state']['desired']['Pwg_TMP']}
   • Bagno Piano Terra: Temperatura attuale: {payload['state']['desired']['PTG_TMP']}
• Informazioni sul clima esterno:
   • Temperatura esterna: {payload['state']['desired']['E_WTD']['current']['temperature']}
   • Umidità esterna: {payload['state']['desired']['E_WTD']['current']['humidity']}%
   • Previsioni per il giorno:
   • Alba: {payload['state']['desired']['E_WTD']['dayDetails']['sunrise']}
   • Tramonto: {payload['state']['desired']['E_WTD']['dayDetails']['sunset']}
   • Temperature minime: {payload['state']['desired']['E_WTD']['dayDetails']['temperatures']['min']}
   • Temperature massime: {payload['state']['desired']['E_WTD']['dayDetails']['temperatures']['max']}
• Localizzazione:
   • Città: 
   • Via: 
   • CAP: 
   • Coordinate geografiche: {payload['state']['desired']['E_LOC']['latLon']}
"""
    print(output)

# Eseguire il login, la chiamata e l'analisi dei dati
if __name__ == "__main__":
    # Ottenere il token di accesso tramite login
    token = login_with_srp(USERNAME, PASSWORD)

    # Eseguire la chiamata GraphQL con il token aggiornato
    if token:
        response_data = make_graphql_call(token)
        if response_data:
            analyze_and_print_data(response_data)

1 Like
	1.	Stato di accensione/spegnimento (ENB):
	•	Il parametro ENB è quello che indica se il dispositivo è acceso o spento.
	•	Nei dati inviati, troviamo i campi come:
	•	"Pca_ENB": 1 → Significa che il dispositivo “Bagno Padronale” è acceso (1 indica acceso, 0 indica spento).
	•	"Pwg_ENB": 0 → Significa che il dispositivo “Bagno Piano Primo” è spento.
	•	"PTG_ENB": 0 → Indica che il dispositivo “Bagno Piano Terra” è spento.
	2.	Temperatura impostata (TSP):
	•	Il campo TSP (che si trova dentro la sezione state.desired) è quello che rappresenta la temperatura impostata.
	•	Ad esempio:
	•	"Pca_TSP": {"p": {"u": 0, "v": 200, "m": 3, "k": "TEMPORARY"}} → Qui la temperatura impostata per il “Bagno Padronale” è 200 (devi dividere per 10, quindi 20.0°C).
	•	"Pwg_TSP": {"p": {"u": 0, "v": 200, "m": 3, "k": "TEMPORARY"}} → Anche per il “Bagno Piano Primo” è impostata a 200 (20.0°C).
	•	"PTG_TSP": {"p": {"u": 0, "v": 200, "m": 3, "k": "TEMPORARY"}} → Temperatura impostata per il “Bagno Piano Terra” a 200 (20.0°C).
1 Like

Hello there,

as promised i’m coming back to you with a further update from IRSAP: sadly that’s not I was hoping to hear.
See below the exchange with the customer service.

*"Buongiorno Sig. *****,

Dopo confronto con il reparto sviluppo del Sistema Now, mi dispiace informarla che al momento non è prevista un’integrazione con Home Assistant.
Sono in fase di valutazione l’implementazione verso altri protocolli, ma non siamo in grado di fornirle ulteriori indicazioni e tempistiche.
Come indicato nelle guide presenti online, è presente un’integrazione con IFTT. Le riporto il link alle guide dove può trovare le possibili operazioni: https://support.irsapnow.com/hc/it/articles/18313863323921-Integrazione-con-IFTTT"

…and my answer…

“La ringrazio per il gentile riscontro, devo dire che è davvero un peccato: l’integrazione con Home Assistant o comunque l’utilizzo di protocolli aperti e universali come Matter è qualcosa che è presente ormai in pressochè qualsiasi dispositivo smart in commercio (compresi prodotti cinesi molto low cost), difficile portare avanti il made in italy quando aziende prestigiose come la Vostra non sanno adattarsi a necessità tecnologiche di base.”

Happy to hear tough that someone with the right skills is trying to solve the issue, if you need some beta testers… here I am

thanks

ciao

fabio

We are well on our way, the repository will be published here and an ad hoc topic will be created, obviously the issues will be to be included in the repository but we expect to publish soon.
There will be a climate type entity to drive power on, power off (antifreeze mode) and of course temperature setting (in the future the reset of a weekly schedule if there is one) and other sensor type entities with some information (e.g. check open window, filpilote, standby, block manual changes)

1 Like

Hello Home Assistant community!

I’m excited to announce the IRSAP Integration for Home Assistant, now available on GitHub! This integration allows you to seamlessly control your IRSAP radiators, enhancing your home’s heating management.

Key Features:

  • Active Scheduling: Manage your radiator schedules effectively.
  • Easy Configuration: Simple setup to get you started in no time.
  • Recent Updates: The latest version (1.1) includes bug fixes and a domain change for improved compatibility, as requested by the brand.

The integration is currently pending approval for HACS, but you can access the repository here: IRSAP Integration GitHub.

I welcome your feedback and suggestions for further improvements. Thank you for your support, and I look forward to hearing from you!

Best,
Valerio

3 Likes

is there a reason it’s supported only from 2024.9 version or can I change it to older versions? I’ve the august one :smile:

Actually it is because we tried with that and not a lower version, you can try it and let us know, so we make the change :grin:

Ok, tried with a fork but I’ve the error when I try to add the integration, not from hacs, but the step after, in hass.

“Impossibile caricare il flusso di configurazione: {“message”:“Invalid handler specified”}”

Try taking the files and copying them inside the custom_components folder and then inside irsap-ha

did that, all the files (*.py and manifest.json) inside custom_components/irsap-ha but same issue

Do you see anything in the logs?

Yes, in config_entries.py:2871
Error occurred loading flow for integration irsap_ha: No module named ‘warrant’

I saw that Cognito is imported from warrant, so maybe some dependencies is missing? Do I need some additional module? For further debug, I’m using the standalone hass, not the os. Could be that?

I tried to manual install warrant, now the error is
Error occurred loading flow for integration irsap_ha: cannot import name ‘Mapping’ from ‘collections’ (/usr/local/lib/python3.12/collections/init.py)