Homey script function vs homey app method

Hi, I have an issue that’s been bothering me for a while, and I can’t figure out where the problem might be. I have a Homey Script for controlling the ventilation system. The script works perfectly fine, but when I call the same logic via a method in the Homey app, it returns a 400 error.

Here is the script:

// Test script for ventilation level
const ip = '192.168.68.64';
const minutes = 15;
const level = 2;

async function setVentilationLevel(minutes, level) {
    try {
        const url = `http://${ip}/JSON/Vars/Ventilation%20timer?index0=0&index1=0&index2=0`;
        console.log(`Setting ventilation level: ${level} for ${minutes} minutes`);
        console.log(url);

        const response = await fetch(url, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json;charset=utf-8',
                'Accept': 'application/json, text/plain, */*',
                'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148',
                'Origin': 'file://',
                'Connection': 'keep-alive',
                'Accept-Language': 'cs-CZ,cs;q=0.9',
                'Accept-Encoding': 'gzip, deflate'
            },
            body: JSON.stringify({ "Value": `${minutes} min Level${level}` })
        });

        if (!response.ok) {
            throw new Error(`Failed to set ventilation level: ${response.statusText}`);
        }

        const data = await response.json();
        console.log('Ventilation level set successfully:', data);
        return data;
    } catch (error) {
        console.error('Error setting ventilation level:', error);
        throw error;
    }
}

// Execute the function
setVentilationLevel(minutes, level).catch(err => console.error(err));

Here is the method:

async setVentilationTimer(minutes, level) {
    try {
        if (minutes < 0 || minutes > 180) {
            throw new Error('Minutes must be between 0 and 180');
        }
        if (level < 1 || level > 4) {
            throw new Error('Level must be between 1 and 4');
        }

        const url = `http://${this.ip}/JSON/Vars/Ventilation%20timer?index0=0&index1=0&index2=0`;
        
        // Exactly the same body construction as in the working script:
        const body = JSON.stringify({ "Value": `${minutes} min Level${level}` });

        const response = await fetch(url, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json;charset=utf-8',
                'Accept': 'application/json, text/plain, */*',
                'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148',
                'Origin': 'file://',
                'Connection': 'keep-alive',
                'Accept-Language': 'cs-CZ,cs;q=0.9',
                'Accept-Encoding': 'gzip, deflate'
            },
            body: body // Using the same stringified value
        });

        if (!response.ok) {
            let errorBody = '';
            try {
                errorBody = await response.text();
            } catch (e) {}
            
            console.error('Request details:', {
                url,
                method: 'POST',
                headers: response.headers,
                rawBody: body,
                error: {
                    status: response.status,
                    statusText: response.statusText,
                    body: errorBody
                }
            });
            throw new Error(`HTTP error! status: ${response.status}`);
        }

        const data = await response.json();
        console.log('Success response:', data);
        return data;
    } catch (error) {
        throw error;
    }
}

The method is called via a flow card defined in device.js like this:

// Register Flow Card Ventilation level
this.setVentilationTimerAction = this.homey.flow.getActionCard('set_ventilation_timer');
this.setVentilationTimerAction.registerRunListener(async (args, state) => {
    console.log('Flow card triggered with args:', args);
    const ip = this.homey.settings.get('endura_ip');
    console.log('Using IP:', ip);
    
    const enduraApi = new EnduraApi(ip);
    try {
        // Log exactly what we're sending
        console.log('Calling API with:', {
            duration: args.duration,
            level: parseInt(args.level),
            rawLevel: args.level
        });
        
        // Try direct value construction first
        const result = await enduraApi.setVentilationTimer(
            Number(args.duration), // Ensure it's a number
            parseInt(args.level)   // Ensure it's a number
        );
        
        console.log('API call result:', result);
        return true;
    } catch (error) {
        this.error('Failed to set ventilation timer. Args:', args, 'Error:', error);
        throw error;
    }
});

Can you help me identify why this works in the script but fails with a 400 error when called through the method?

It was a bit of a pain because, for some reason, node-fetch works perfectly fine for getting data from Endura Delta unit, but it doesn’t work for POST requests to Endura. The same request works fine from a Homey script and Postman, but not from the app. :smiley: That’s why I had to use HTTP instead of fetchfor POST requests.

1 Like