Spot-hinta.fi - Yksinkertainen ohjelmistorajapinta (API) sähkön pörssihinnan hakemiseen

kayttajatunnus

Aktiivinen jäsen
Äkkiä katsoen parametrit on oikein. Kun skriptin käynnistää, ajetaan tosiaan molemmat säännöt ja lauantain sääntö jää voimaan.

Mutta seuraavalla tunnilla joa ei ole lauantai, ei se sääntö tee mitään koska vastaus on palvelimelta varmasti "BadRequest" ja se sääntö kun edellisellä tunnilla oli jo BadRequest, ei lauantai sääntö tee mitään.

Eli vain ensimmäisellä tunnilla skripti toimii väärin. Sitten se menee rytmiinsä ja homman pitäisi pelittää oikein.
Kyllä se näin on tosiaan näyttänyt menneen eli liian hätäinen olin.

Kiitokset taas :)
 

kayttajatunnus

Aktiivinen jäsen
Jotain hämmentävää oli nyt kuitenkin tapahtunut viime yönä tuon skriptini kanssa. Tein siihen inverted muutokset eli kun rele vetää, lämmintä vettä ei tuoteta.

Rank on 4 eli seuraavat tunnit olisi releen pitänyt olla pois päältä: 00-01, 03-06

Lokista näkyy että rele on toiminut seuraavasti:
00:00:52: off
00:00:53: on
03:00:41: off
04:00:36: on

Eli keskiyöllä rele on räpsäissyt on-off-on ja 03-04 välisen ajan lämmittänyt vettä.

Mielestäni tämä toimi jo kun kokeilin ilman minkään laitteen ohjausta.

Koodi:
More information about the API (in Finnish): https://spot-hinta.fi/
// Support Shelly script and Spot-hinta.fi API development and maintenance: https://www.buymeacoffee.com/spothintafi

// This script can control up to four different relays:
// - Max two relays with PRICE limit only
// - Max two relays with RANK (number of cheapest hours) AND PRICE limit

// Region to use. See supported regions in Swagger documentation: https://api.spot-hinta.fi/swagger/ui
let Region = "FI";

// Settings - Price limit - rule 1
let SETTINGS_PRICELIMIT_1 =
{
    // User settings
    RelayIsInUse: false, // True/false: If you want to use this relay or not
    PriceAllowed: "30", // Price limit (in euro cents, without decimals). Use "-99" if not wanted. If price is now less than this the relay is turned ON (or OFF if inverted - see below)
    AllowedDays: "1,2,3,4,5,6,7", // Execution days: 1=Monday to 7=Sunday, separated with comma.
    Relay: "0",  // Shelly's relay number (Value is between 0-3 depending how many relays Shelly has). Make sure this is correct!
    RelayName: "OilBoiler",  // Name this relay. Name is used in debug log mostly.
    Inverted: false, // True/false: Set to "true" to inverted the relay logic

    // Script technical fields. Do not edit!
    Url: 1,
    SettingsNumber: 1,
    RelayStatus: true,
    RelayStatusSource: "",
    RelayExecuted: false,
};

// Settings - Price limit - rule 2
let SETTINGS_PRICELIMIT_2 =
{
    // User settings
    RelayIsInUse: false,
    PriceAllowed: "20",
    AllowedDays: "1,2,3,4,5,6,7",
    Relay: "0",
    RelayName: "Charger",
    Inverted: false,

    // Script technical fields. Do not edit!
    Url: 1,
    SettingsNumber: 2,
    RelayStatus: true,
    RelayStatusSource: "",
    RelayExecuted: false,
};

// Settings - Rank and price limit - rule 1
let SETTINGS_RANK_PRICE_1 =
{
    // User settings
    RelayIsInUse: true,
    Rank: "4", // "Rank" limit (number of cheapest hours today)
    PriceAllowed: "2",
    MaxPrice: "999", // Maximum allowed price in euro cents.
    AllowedDays: "1,2,3,4,5,7",
    BackupHours: ["00", "01", "02", "03", "04", "05"], // Backup hours; if API is not answering or internet connection is down.
    BoosterHours: "99,99", // Relay is always ON during booster hours. Use "99,99" to disable this.
    PriorityHours: "99,99", // Hours you want to prioritize. If PriceModifier: is "0" these hours always get the smallest 'rank'. Use "99,99" to disable this.
    PriorityHoursRank: "3",  // How many priority hours are prioritized. i.e. "3" = 3 cheapest priority hours.
    PriceModifier: "0", // If priority hours have lower price - such as 'night electricity' - the difference in Euro cents.
    Relay: "0",
    RelayName: "WaterHeater_normaali",
    Inverted: true,

    // Script technical fields. Do not edit!
    Url: 2,
    SettingsNumber: 3,
    RelayStatus: true,
    RelayStatusSource: "",
    RelayExecuted: false,
};

// Settings - Rank and price limit - rule 2
let SETTINGS_RANK_PRICE_2 =
{
    // User settings
    RelayIsInUse: true,
    Rank: "5",
    PriceAllowed: "2",
    MaxPrice: "999",
    AllowedDays: "6",
    BackupHours: ["01", "02", "03", "04", "05"],
    BoosterHours: "99,99",
    PriorityHours: "01,02,03,04,05",
    PriorityHoursRank: "5",
    PriceModifier: "0",
    Relay: "0",
    RelayName: "WaterHeater_legionella",
    Inverted: true,

    // Script technical fields. Do not edit!
    Url: 2,
    SettingsNumber: 4,
    RelayStatus: true,
    RelayStatusSource: "",
    RelayExecuted: false,
};

// ------------------------------------
// Script - no need to modify (usually)
// ------------------------------------

// Variables needed to control the execution
let currentHour = "";
let currentHourUpdated = "";
let rounds = 0;

// This is triggered by the timer (see end of the script)
function ExecuteRelayRules() {

    // Counter of exeutution rounds. First round means mostly.
    rounds = rounds + 1;

    // Update current hour in a global variable.
    UpdateCurrentHour(rounds);

    // Reset relays if hour has changed
    InitializeRelaysIfHourHasChanged(rounds);

    // This was initialization round. Next round is the first actual processing round.
    if (rounds === 1) { print("Script initialization done."); return; }

    // Return if current hour is already done.
    if (HasCurrentHourBeenDone() === true) {
        return;
    }

    // Execute Relays which are in use and not executed yet.
    ExecuteRelayRule(SETTINGS_PRICELIMIT_1);
    ExecuteRelayRule(SETTINGS_PRICELIMIT_2);
    ExecuteRelayRule(SETTINGS_RANK_PRICE_1);
    ExecuteRelayRule(SETTINGS_RANK_PRICE_2);
}

// Executes HTTP GET query for a given settings
function ExecuteRelayRule(Settings) {

    // Do nothing if relay is not in use or it is already executed for this hour
    if (Settings.RelayIsInUse === false || Settings.RelayExecuted === true) { return; }

    // Get URL
    let urlToCall = BuildUrl(Settings);

    // HTTP Get Request to API
    print("Execute HTTP GET. Relay name: " + Settings.RelayName + " - URL: " + urlToCall);
    Shelly.call("HTTP.Request", { method: "GET", url: urlToCall, timeout: 10, ssl_ca: "*" }, ProcessHttpRequestResponse, Settings);
}

// Process response for HTTP GET
function ProcessHttpRequestResponse(response, error_code, error_msg, Settings) {

    // Process response and get status if processing was done successfully.
    let response = SetRelayStatusInShellyBasedOnHttpStatus(response, error_code, error_msg, Settings);

    // Update relay executed status
    if (Settings.SettingsNumber === 1) { SETTINGS_PRICELIMIT_1.RelayExecuted = response; }
    if (Settings.SettingsNumber === 2) { SETTINGS_PRICELIMIT_2.RelayExecuted = response; }
    if (Settings.SettingsNumber === 3) { SETTINGS_RANK_PRICE_1.RelayExecuted = response; }
    if (Settings.SettingsNumber === 4) { SETTINGS_RANK_PRICE_2.RelayExecuted = response; }
}

// Control the relays based on the result from the API call
function SetRelayStatusInShellyBasedOnHttpStatus(response, error_code, error_msg, Settings) {

    // Check for network errors
    if (error_code !== 0 || response === null) {
        print("Network error occurred: " + error_msg);
        RunBackupHourRule(Settings);
        return false;
    }

    if (response.code === 200) {
        SetRelayStatusInShelly(Settings, true, "api"); // Relay is turned on.
        return true;
    }
    else if (response.code === 400) {
        SetRelayStatusInShelly(Settings, false, "api"); // Relay is turned off.
        return true;
    }
    else {
        print("HTTP status code does not indicate success. HTTP status code: " + JSON.stringify(response.code));
        RunBackupHourRule(Settings);
        return false;
    }
}

// Change relay status in Shelly
function SetRelayStatusInShelly(Settings, newStatus, relayStatusSource) {

    // Set status messages
    let statusMessage = "";
    if (newStatus === true && Settings.Inverted === true) { statusMessage = "Turn relay off (inverted). Relay name: " + Settings.RelayName; }
    if (newStatus === false && Settings.Inverted === true) { statusMessage = "Turn relay on (inverted). Relay name: " + Settings.RelayName; }
    if (newStatus === true && Settings.Inverted === false) { statusMessage = "Turn relay on. Relay name: " + Settings.RelayName; }
    if (newStatus === false && Settings.Inverted === false) { statusMessage = "Turn relay off. Relay name: " + Settings.RelayName; }

    // Switch behavior based on Inverted-setting
    if (Settings.Inverted === true && newStatus === true) { newStatus = false; }
    else if (Settings.Inverted === true && newStatus === false) { newStatus = true; }

    // Don't close relay if it is already registered as closed AND source in last control is 'api'
    // When closing has been done by 'backupHour', relay can be closed again.
    if (Settings.RelayStatus === false && newStatus === false && Settings.RelayStatusSource === "api") {
        print("Relay is already closed. Not closing again (this allows possible other script to act)");
        return;
    }

    // Set relay in Shelly
    print(statusMessage);
    Shelly.call("Switch.Set", "{ id:" + Settings.Relay + ", on:" + JSON.stringify(newStatus) + "}", null, null);

    // Save status information into settings
    SetRelayStatusInSettings(Settings, newStatus, relayStatusSource);
}

// Set relay status for a given settings
function SetRelayStatusInSettings(Settings, newStatus, relayStatusSource) {

    if (Settings.SettingsNumber === 1) { SETTINGS_PRICELIMIT_1.RelayStatus = newStatus; SETTINGS_PRICELIMIT_1.RelayStatusSource = relayStatusSource; }
    if (Settings.SettingsNumber === 2) { SETTINGS_PRICELIMIT_2.RelayStatus = newStatus; SETTINGS_PRICELIMIT_2.RelayStatusSource = relayStatusSource; }
    if (Settings.SettingsNumber === 3) { SETTINGS_RANK_PRICE_1.RelayStatus = newStatus; SETTINGS_RANK_PRICE_1.RelayStatusSource = relayStatusSource; }
    if (Settings.SettingsNumber === 4) { SETTINGS_RANK_PRICE_2.RelayStatus = newStatus; SETTINGS_RANK_PRICE_2.RelayStatusSource = relayStatusSource; }
}

// This is executed if API did not respond properly. This is NOT considered as successful execution
function RunBackupHourRule(Settings) {

    // Check if settings has BackupHours property
    let hasBackupRules = false;
    for (let property in Settings) {
        if (property === "BackupHours") { hasBackupRules = true; }
    }

    // Return if not configured
    if (hasBackupRules === false) { print("This setting does not have BackupHours property. Relay name: " + Settings.RelayName); return; }

    // Check if current hour is listed in backup hours
    let currentHourIsListed = false;
    for (let i = 0; i < Settings.BackupHours.length; i++) {
        if (Settings.BackupHours[i] === currentHour) { currentHourIsListed = true; }
    }

    // When current hour has not been updated for some reason, it might be better to consider this as backup hour
    // Comment this line, if you don't like the behavior.
    if (currentHour === null || currentHour === "") { currentHourIsListed = true; }

    // Set Shelly relay according if hour is backup hour.
    print("Executing backup rule for relay: " + Settings.RelayName + " - Current hour: " + currentHour + " - Current hour is backup hour: " + JSON.stringify(currentHourIsListed));
    SetRelayStatusInShelly(Settings, currentHourIsListed, "backupHour");
}

// Get the current hour and put it in global variable
function UpdateCurrentHour(rounds) {

    Shelly.call("Shelly.GetStatus", "", function (res, rounds) {
        if (res.sys.time !== null) {
            currentHourUpdated = res.sys.time.slice(0, 2);  // f.ex. "21:34"
            if (rounds === 1) { currentHour = currentHourUpdated; }
        }
        else {
            currentHourUpdated = ""; // Time is null if Shelly does not have connection to time server
        }
    }, rounds);
}

// Initialize relay statuses if hour has changed
function InitializeRelaysIfHourHasChanged(rounds) {

    if (currentHour !== currentHourUpdated || rounds === 1) {
        // Update current hour
        currentHour = currentHourUpdated;

        // Skip relays which are not in use - set their "RelayExecuted" state to true.
        if (SETTINGS_PRICELIMIT_1.RelayIsInUse === true) { SETTINGS_PRICELIMIT_1.RelayExecuted = false } else { SETTINGS_PRICELIMIT_1.RelayExecuted = true; };
        if (SETTINGS_PRICELIMIT_2.RelayIsInUse === true) { SETTINGS_PRICELIMIT_2.RelayExecuted = false } else { SETTINGS_PRICELIMIT_2.RelayExecuted = true; };
        if (SETTINGS_RANK_PRICE_1.RelayIsInUse === true) { SETTINGS_RANK_PRICE_1.RelayExecuted = false } else { SETTINGS_RANK_PRICE_1.RelayExecuted = true; };
        if (SETTINGS_RANK_PRICE_2.RelayIsInUse === true) { SETTINGS_RANK_PRICE_2.RelayExecuted = false } else { SETTINGS_RANK_PRICE_2.RelayExecuted = true; };
    }
}

// Check if all relays are done for this hour.
function HasCurrentHourBeenDone() {

    if (SETTINGS_PRICELIMIT_1.RelayExecuted === true &&
        SETTINGS_PRICELIMIT_2.RelayExecuted === true &&
        SETTINGS_RANK_PRICE_1.RelayExecuted === true &&
        SETTINGS_RANK_PRICE_2.RelayExecuted === true) {
        print("Current hour is already done.");
        return true;
    }

    return false;
}

// Builds URL to call the API
function BuildUrl(Settings) {

    // Price limit URL
    if (Settings.Url === 1) {
        return "https://api.spot-hinta.fi/JustNow/" + Settings.PriceAllowed + "?region=" + Region + "&allowedDays=" + Settings.AllowedDays;
    }

    // Price and Rank limit URL
    if (Settings.Url === 2) {
        let url = "https://api.spot-hinta.fi/JustNowRank/" + Settings.Rank + "/" + Settings.PriceAllowed;
        url += "?maxPrice=" + Settings.MaxPrice;
        url += "&allowedDays=" + Settings.AllowedDays;
        url += "&boosterHours=" + Settings.BoosterHours;
        url += "&priorityHours=" + Settings.PriorityHours;
        url += "&priorityHoursRank=" + Settings.PriorityHoursRank;
        url += "&priceModifier=" + Settings.PriceModifier;
        url += "&region=" + Region;
        return url;
    }

    return "";
}

// Main timer to execute rules
Timer.set(30000, true, ExecuteRelayRules);
 

kayttajatunnus

Aktiivinen jäsen
Mulle paras scripti ois sellainen että 22-07 välillä rele vetäis muina paitsi x halvimpana tuntina ja 07-22 välillä rele vetäis jos x hinta ylittyy. Eli yöllä varataan lattioita halvimpina tunteina ja päivällä lämmitetään jos se ei ole kallista. Rele ohjaa siis sähkölämmityksen sulkuaikatuloa estäen kalliin sähkön käytön.
En tiedä onko tässä jotain muita muuttujia vielä mutta itse tekisin tämän tällä skriptillä: https://github.com/Spot-hinta-fi/Shelly/blob/main/Scripts/Shelly-Rank_and_Price_limit.js

Siihen voi laittaa yö ja päiväsähkön hinnan erotuksen (eli varmaankin siirron erotus yösähkön hyväksi) sekä myös maksimihinnan, minkä alla lämmitetään aina.

Voihan sen lämmityksen päivälläkin tehdä 7-22 välillä, jos se on halvempaa kuin yöllä?
 

Jimi

Jäsen
En tiedä onko tässä jotain muita muuttujia vielä mutta itse tekisin tämän tällä skriptillä: https://github.com/Spot-hinta-fi/Shelly/blob/main/Scripts/Shelly-Rank_and_Price_limit.js

Siihen voi laittaa yö ja päiväsähkön hinnan erotuksen (eli varmaankin siirron erotus yösähkön hyväksi) sekä myös maksimihinnan, minkä alla lämmitetään aina.

Voihan sen lämmityksen päivälläkin tehdä 7-22 välillä, jos se on halvempaa kuin yöllä?
Pääosin lämmitystä ei voi tehdä päivällä kun silloin varaavanlattian kaapelit ei ole kytkettynä vaan lisälämmittää suorana kattokalvoilla joiden tehot paljon pienemmät. Toki olisi mahdollista lisätä rele joka pakottaa lämmityksen lattiaan päivälläkin tai jättää kattolämmitys pois käytöstä kokonaan.
 

kayttajatunnus

Aktiivinen jäsen
Jotain hämmentävää oli nyt kuitenkin tapahtunut viime yönä tuon skriptini kanssa. Tein siihen inverted muutokset eli kun rele vetää, lämmintä vettä ei tuoteta.

Rank on 4 eli seuraavat tunnit olisi releen pitänyt olla pois päältä: 00-01, 03-06

Lokista näkyy että rele on toiminut seuraavasti:
00:00:52: off
00:00:53: on
03:00:41: off
04:00:36: on

Eli keskiyöllä rele on räpsäissyt on-off-on ja 03-04 välisen ajan lämmittänyt vettä.

Mielestäni tämä toimi jo kun kokeilin ilman minkään laitteen ohjausta.

Koodi:
More information about the API (in Finnish): https://spot-hinta.fi/
// Support Shelly script and Spot-hinta.fi API development and maintenance: https://www.buymeacoffee.com/spothintafi

// This script can control up to four different relays:
// - Max two relays with PRICE limit only
// - Max two relays with RANK (number of cheapest hours) AND PRICE limit

// Region to use. See supported regions in Swagger documentation: https://api.spot-hinta.fi/swagger/ui
let Region = "FI";

// Settings - Price limit - rule 1
let SETTINGS_PRICELIMIT_1 =
{
    // User settings
    RelayIsInUse: false, // True/false: If you want to use this relay or not
    PriceAllowed: "30", // Price limit (in euro cents, without decimals). Use "-99" if not wanted. If price is now less than this the relay is turned ON (or OFF if inverted - see below)
    AllowedDays: "1,2,3,4,5,6,7", // Execution days: 1=Monday to 7=Sunday, separated with comma.
    Relay: "0",  // Shelly's relay number (Value is between 0-3 depending how many relays Shelly has). Make sure this is correct!
    RelayName: "OilBoiler",  // Name this relay. Name is used in debug log mostly.
    Inverted: false, // True/false: Set to "true" to inverted the relay logic

    // Script technical fields. Do not edit!
    Url: 1,
    SettingsNumber: 1,
    RelayStatus: true,
    RelayStatusSource: "",
    RelayExecuted: false,
};

// Settings - Price limit - rule 2
let SETTINGS_PRICELIMIT_2 =
{
    // User settings
    RelayIsInUse: false,
    PriceAllowed: "20",
    AllowedDays: "1,2,3,4,5,6,7",
    Relay: "0",
    RelayName: "Charger",
    Inverted: false,

    // Script technical fields. Do not edit!
    Url: 1,
    SettingsNumber: 2,
    RelayStatus: true,
    RelayStatusSource: "",
    RelayExecuted: false,
};

// Settings - Rank and price limit - rule 1
let SETTINGS_RANK_PRICE_1 =
{
    // User settings
    RelayIsInUse: true,
    Rank: "4", // "Rank" limit (number of cheapest hours today)
    PriceAllowed: "2",
    MaxPrice: "999", // Maximum allowed price in euro cents.
    AllowedDays: "1,2,3,4,5,7",
    BackupHours: ["00", "01", "02", "03", "04", "05"], // Backup hours; if API is not answering or internet connection is down.
    BoosterHours: "99,99", // Relay is always ON during booster hours. Use "99,99" to disable this.
    PriorityHours: "99,99", // Hours you want to prioritize. If PriceModifier: is "0" these hours always get the smallest 'rank'. Use "99,99" to disable this.
    PriorityHoursRank: "3",  // How many priority hours are prioritized. i.e. "3" = 3 cheapest priority hours.
    PriceModifier: "0", // If priority hours have lower price - such as 'night electricity' - the difference in Euro cents.
    Relay: "0",
    RelayName: "WaterHeater_normaali",
    Inverted: true,

    // Script technical fields. Do not edit!
    Url: 2,
    SettingsNumber: 3,
    RelayStatus: true,
    RelayStatusSource: "",
    RelayExecuted: false,
};

// Settings - Rank and price limit - rule 2
let SETTINGS_RANK_PRICE_2 =
{
    // User settings
    RelayIsInUse: true,
    Rank: "5",
    PriceAllowed: "2",
    MaxPrice: "999",
    AllowedDays: "6",
    BackupHours: ["01", "02", "03", "04", "05"],
    BoosterHours: "99,99",
    PriorityHours: "01,02,03,04,05",
    PriorityHoursRank: "5",
    PriceModifier: "0",
    Relay: "0",
    RelayName: "WaterHeater_legionella",
    Inverted: true,

    // Script technical fields. Do not edit!
    Url: 2,
    SettingsNumber: 4,
    RelayStatus: true,
    RelayStatusSource: "",
    RelayExecuted: false,
};

// ------------------------------------
// Script - no need to modify (usually)
// ------------------------------------

// Variables needed to control the execution
let currentHour = "";
let currentHourUpdated = "";
let rounds = 0;

// This is triggered by the timer (see end of the script)
function ExecuteRelayRules() {

    // Counter of exeutution rounds. First round means mostly.
    rounds = rounds + 1;

    // Update current hour in a global variable.
    UpdateCurrentHour(rounds);

    // Reset relays if hour has changed
    InitializeRelaysIfHourHasChanged(rounds);

    // This was initialization round. Next round is the first actual processing round.
    if (rounds === 1) { print("Script initialization done."); return; }

    // Return if current hour is already done.
    if (HasCurrentHourBeenDone() === true) {
        return;
    }

    // Execute Relays which are in use and not executed yet.
    ExecuteRelayRule(SETTINGS_PRICELIMIT_1);
    ExecuteRelayRule(SETTINGS_PRICELIMIT_2);
    ExecuteRelayRule(SETTINGS_RANK_PRICE_1);
    ExecuteRelayRule(SETTINGS_RANK_PRICE_2);
}

// Executes HTTP GET query for a given settings
function ExecuteRelayRule(Settings) {

    // Do nothing if relay is not in use or it is already executed for this hour
    if (Settings.RelayIsInUse === false || Settings.RelayExecuted === true) { return; }

    // Get URL
    let urlToCall = BuildUrl(Settings);

    // HTTP Get Request to API
    print("Execute HTTP GET. Relay name: " + Settings.RelayName + " - URL: " + urlToCall);
    Shelly.call("HTTP.Request", { method: "GET", url: urlToCall, timeout: 10, ssl_ca: "*" }, ProcessHttpRequestResponse, Settings);
}

// Process response for HTTP GET
function ProcessHttpRequestResponse(response, error_code, error_msg, Settings) {

    // Process response and get status if processing was done successfully.
    let response = SetRelayStatusInShellyBasedOnHttpStatus(response, error_code, error_msg, Settings);

    // Update relay executed status
    if (Settings.SettingsNumber === 1) { SETTINGS_PRICELIMIT_1.RelayExecuted = response; }
    if (Settings.SettingsNumber === 2) { SETTINGS_PRICELIMIT_2.RelayExecuted = response; }
    if (Settings.SettingsNumber === 3) { SETTINGS_RANK_PRICE_1.RelayExecuted = response; }
    if (Settings.SettingsNumber === 4) { SETTINGS_RANK_PRICE_2.RelayExecuted = response; }
}

// Control the relays based on the result from the API call
function SetRelayStatusInShellyBasedOnHttpStatus(response, error_code, error_msg, Settings) {

    // Check for network errors
    if (error_code !== 0 || response === null) {
        print("Network error occurred: " + error_msg);
        RunBackupHourRule(Settings);
        return false;
    }

    if (response.code === 200) {
        SetRelayStatusInShelly(Settings, true, "api"); // Relay is turned on.
        return true;
    }
    else if (response.code === 400) {
        SetRelayStatusInShelly(Settings, false, "api"); // Relay is turned off.
        return true;
    }
    else {
        print("HTTP status code does not indicate success. HTTP status code: " + JSON.stringify(response.code));
        RunBackupHourRule(Settings);
        return false;
    }
}

// Change relay status in Shelly
function SetRelayStatusInShelly(Settings, newStatus, relayStatusSource) {

    // Set status messages
    let statusMessage = "";
    if (newStatus === true && Settings.Inverted === true) { statusMessage = "Turn relay off (inverted). Relay name: " + Settings.RelayName; }
    if (newStatus === false && Settings.Inverted === true) { statusMessage = "Turn relay on (inverted). Relay name: " + Settings.RelayName; }
    if (newStatus === true && Settings.Inverted === false) { statusMessage = "Turn relay on. Relay name: " + Settings.RelayName; }
    if (newStatus === false && Settings.Inverted === false) { statusMessage = "Turn relay off. Relay name: " + Settings.RelayName; }

    // Switch behavior based on Inverted-setting
    if (Settings.Inverted === true && newStatus === true) { newStatus = false; }
    else if (Settings.Inverted === true && newStatus === false) { newStatus = true; }

    // Don't close relay if it is already registered as closed AND source in last control is 'api'
    // When closing has been done by 'backupHour', relay can be closed again.
    if (Settings.RelayStatus === false && newStatus === false && Settings.RelayStatusSource === "api") {
        print("Relay is already closed. Not closing again (this allows possible other script to act)");
        return;
    }

    // Set relay in Shelly
    print(statusMessage);
    Shelly.call("Switch.Set", "{ id:" + Settings.Relay + ", on:" + JSON.stringify(newStatus) + "}", null, null);

    // Save status information into settings
    SetRelayStatusInSettings(Settings, newStatus, relayStatusSource);
}

// Set relay status for a given settings
function SetRelayStatusInSettings(Settings, newStatus, relayStatusSource) {

    if (Settings.SettingsNumber === 1) { SETTINGS_PRICELIMIT_1.RelayStatus = newStatus; SETTINGS_PRICELIMIT_1.RelayStatusSource = relayStatusSource; }
    if (Settings.SettingsNumber === 2) { SETTINGS_PRICELIMIT_2.RelayStatus = newStatus; SETTINGS_PRICELIMIT_2.RelayStatusSource = relayStatusSource; }
    if (Settings.SettingsNumber === 3) { SETTINGS_RANK_PRICE_1.RelayStatus = newStatus; SETTINGS_RANK_PRICE_1.RelayStatusSource = relayStatusSource; }
    if (Settings.SettingsNumber === 4) { SETTINGS_RANK_PRICE_2.RelayStatus = newStatus; SETTINGS_RANK_PRICE_2.RelayStatusSource = relayStatusSource; }
}

// This is executed if API did not respond properly. This is NOT considered as successful execution
function RunBackupHourRule(Settings) {

    // Check if settings has BackupHours property
    let hasBackupRules = false;
    for (let property in Settings) {
        if (property === "BackupHours") { hasBackupRules = true; }
    }

    // Return if not configured
    if (hasBackupRules === false) { print("This setting does not have BackupHours property. Relay name: " + Settings.RelayName); return; }

    // Check if current hour is listed in backup hours
    let currentHourIsListed = false;
    for (let i = 0; i < Settings.BackupHours.length; i++) {
        if (Settings.BackupHours[i] === currentHour) { currentHourIsListed = true; }
    }

    // When current hour has not been updated for some reason, it might be better to consider this as backup hour
    // Comment this line, if you don't like the behavior.
    if (currentHour === null || currentHour === "") { currentHourIsListed = true; }

    // Set Shelly relay according if hour is backup hour.
    print("Executing backup rule for relay: " + Settings.RelayName + " - Current hour: " + currentHour + " - Current hour is backup hour: " + JSON.stringify(currentHourIsListed));
    SetRelayStatusInShelly(Settings, currentHourIsListed, "backupHour");
}

// Get the current hour and put it in global variable
function UpdateCurrentHour(rounds) {

    Shelly.call("Shelly.GetStatus", "", function (res, rounds) {
        if (res.sys.time !== null) {
            currentHourUpdated = res.sys.time.slice(0, 2);  // f.ex. "21:34"
            if (rounds === 1) { currentHour = currentHourUpdated; }
        }
        else {
            currentHourUpdated = ""; // Time is null if Shelly does not have connection to time server
        }
    }, rounds);
}

// Initialize relay statuses if hour has changed
function InitializeRelaysIfHourHasChanged(rounds) {

    if (currentHour !== currentHourUpdated || rounds === 1) {
        // Update current hour
        currentHour = currentHourUpdated;

        // Skip relays which are not in use - set their "RelayExecuted" state to true.
        if (SETTINGS_PRICELIMIT_1.RelayIsInUse === true) { SETTINGS_PRICELIMIT_1.RelayExecuted = false } else { SETTINGS_PRICELIMIT_1.RelayExecuted = true; };
        if (SETTINGS_PRICELIMIT_2.RelayIsInUse === true) { SETTINGS_PRICELIMIT_2.RelayExecuted = false } else { SETTINGS_PRICELIMIT_2.RelayExecuted = true; };
        if (SETTINGS_RANK_PRICE_1.RelayIsInUse === true) { SETTINGS_RANK_PRICE_1.RelayExecuted = false } else { SETTINGS_RANK_PRICE_1.RelayExecuted = true; };
        if (SETTINGS_RANK_PRICE_2.RelayIsInUse === true) { SETTINGS_RANK_PRICE_2.RelayExecuted = false } else { SETTINGS_RANK_PRICE_2.RelayExecuted = true; };
    }
}

// Check if all relays are done for this hour.
function HasCurrentHourBeenDone() {

    if (SETTINGS_PRICELIMIT_1.RelayExecuted === true &&
        SETTINGS_PRICELIMIT_2.RelayExecuted === true &&
        SETTINGS_RANK_PRICE_1.RelayExecuted === true &&
        SETTINGS_RANK_PRICE_2.RelayExecuted === true) {
        print("Current hour is already done.");
        return true;
    }

    return false;
}

// Builds URL to call the API
function BuildUrl(Settings) {

    // Price limit URL
    if (Settings.Url === 1) {
        return "https://api.spot-hinta.fi/JustNow/" + Settings.PriceAllowed + "?region=" + Region + "&allowedDays=" + Settings.AllowedDays;
    }

    // Price and Rank limit URL
    if (Settings.Url === 2) {
        let url = "https://api.spot-hinta.fi/JustNowRank/" + Settings.Rank + "/" + Settings.PriceAllowed;
        url += "?maxPrice=" + Settings.MaxPrice;
        url += "&allowedDays=" + Settings.AllowedDays;
        url += "&boosterHours=" + Settings.BoosterHours;
        url += "&priorityHours=" + Settings.PriorityHours;
        url += "&priorityHoursRank=" + Settings.PriorityHoursRank;
        url += "&priceModifier=" + Settings.PriceModifier;
        url += "&region=" + Region;
        return url;
    }

    return "";
}

// Main timer to execute rules
Timer.set(30000, true, ExecuteRelayRules);
Nyt täytyy sanoa, että en saa tätä mitenkään toimimaan kunnolla, jos on samalle releelle omat skriptiosionsa eri päiville.

Laitoin nyt vain toisen noista päälle, niin vaikuttaa siltä että heti lähti toimimaan oikein. Aiemmin en saanut edes hintarajaa toimimaan.
 

kayttajatunnus

Aktiivinen jäsen
Mitenkähän vaikea olisi muuten lisätä skripteihin ennakko MLP asteminuuttilaskentaa varten? Aika moni taitaa käyttää näitä MLP:n kanssa siten, että asteminuuttien laskenta nollautuu ja kun skripti napsahtaa tasatunnilla päälle, vasta silloin alkaa asteminuuttilaskenta.

Mutta jos sen laskennan saisikin päälle jo esim 10min ennen tasatuntia, saisi halvasta tunnista paremman hyödyn irti.
 

Nippis

Tulokas
testailin tätä ohjelmaa shellyssä: https://github.com/Spot-hinta-fi/Shelly/blob/main/Scripts/Shelly-Rank_and_Price_limit.j.
Saako tämän toimimaan niin, että etsii yösähkön aikaan eli 23-7 aikana tarvittavat lämmitys tunnit? Yritin PriorityHours:iin laittaa tunteja kuten kuten BackupHours mutta en saanut toimimaan, ilmoitti errorista!!!
Löytyiskö apuja minun ongelmaan?

// User settings
RelayIsInUse: true,
Rank: "5", // "Rank" limit (number of cheapest hours today)
PriceAllowed: "0",
MaxPrice: "999", // Maximum allowed price in euro cents.
AllowedDays: "1,2,3,4,5,6,7",
BackupHours: ["12", "13", "14", "03", "05"], // Backup hours; if API is not answering or internet connection is down.
BoosterHours: "99,99", // Relay is always ON during booster hours. Use "99,99" to disable this.
PriorityHours:"99,99", // Hours you want to prioritize. If PriceModifier: is "0" these hours always get the smallest 'rank'. Use "99,99" to disable this.
PriorityHoursRank: "3", // How many priority hours are prioritized. i.e. "3" = 3 cheapest priority hours.
PriceModifier: "0", // If priority hours have lower price - such as 'night electricity' - the difference in Euro cents.
Relay: "0",
RelayName: "Kuumavesivaraaja",
Inverted: false,

YT Nippis
 

Mikki

Hyperaktiivi
testailin tätä ohjelmaa shellyssä: https://github.com/Spot-hinta-fi/Shelly/blob/main/Scripts/Shelly-Rank_and_Price_limit.j.
Saako tämän toimimaan niin, että etsii yösähkön aikaan eli 23-7 aikana tarvittavat lämmitys tunnit? Yritin PriorityHours:iin laittaa tunteja kuten kuten BackupHours mutta en saanut toimimaan, ilmoitti errorista!!!
Löytyiskö apuja minun ongelmaan?

Löytyy apua... otappas tämä lyhyempi ja yksinkertaisempi skripti jos yöajalta haluat halvimmat tunnit:

Riittää kun asetat vain tuntimäärän "rank" kohtaan, niin se hakee yösähköajalta halvimmat tunnit.
 

kayttajatunnus

Aktiivinen jäsen
testailin tätä ohjelmaa shellyssä: https://github.com/Spot-hinta-fi/Shelly/blob/main/Scripts/Shelly-Rank_and_Price_limit.j.
Saako tämän toimimaan niin, että etsii yösähkön aikaan eli 23-7 aikana tarvittavat lämmitys tunnit? Yritin PriorityHours:iin laittaa tunteja kuten kuten BackupHours mutta en saanut toimimaan, ilmoitti errorista!!!
Löytyiskö apuja minun ongelmaan?

// User settings
RelayIsInUse: true,
Rank: "5", // "Rank" limit (number of cheapest hours today)
PriceAllowed: "0",
MaxPrice: "999", // Maximum allowed price in euro cents.
AllowedDays: "1,2,3,4,5,6,7",
BackupHours: ["12", "13", "14", "03", "05"], // Backup hours; if API is not answering or internet connection is down.
BoosterHours: "99,99", // Relay is always ON during booster hours. Use "99,99" to disable this.
PriorityHours:"99,99", // Hours you want to prioritize. If PriceModifier: is "0" these hours always get the smallest 'rank'. Use "99,99" to disable this.
PriorityHoursRank: "3", // How many priority hours are prioritized. i.e. "3" = 3 cheapest priority hours.
PriceModifier: "0", // If priority hours have lower price - such as 'night electricity' - the difference in Euro cents.
Relay: "0",
RelayName: "Kuumavesivaraaja",
Inverted: false,

YT Nippis
Mitä jos tässäkin käyttäisi tuota price modifieria ja lämmittäisi pelkän yösähkön sijaan silloin kun lämmittäminen on halvinta?
 

lassiko

Jäsen
Alle olevaa erroria nyt pukannut pitkään HA käynnistyksen yhteydessä.

Käytössä T3m3z/spotprices2ha spot_price.yaml v0.1.11 eli tuorein. Onko vinkkejä miten tästä pääsisi eteenpäin?


Error while processing template: Template<template=({{ (states("sensor.shf_control_factor_1") | float /2 + 0.5) | default(none) }}) renders=2>
13:19:01 – (ERROR) helpers/template.py - message first occurred at 13:19:01 and shows up 17 times

Logger: homeassistant.helpers.event
Source: helpers/template.py:565
First occurred: 13:19:01 (17 occurrences)
Last logged: 13:19:01

Error while processing template: Template<template=({{ (states("sensor.shf_control_factor_1") | float /2 + 0.5) | default(none) }}) renders=2>
Error while processing template: Template<template=({% set output = namespace(value=[]) %} {% for value in state_attr("sensor.shf_control_factor_1", "today_values") %} {% set output.value = output.value + [ value | float /2 + 0.5 ] %} {% endfor %} {{ output.value }}) renders=2>
Error while processing template: Template<template=({% set output = namespace(value=[]) %} {% for pnow in state_attr("sensor.shf_electricity_price_now", "today_prices") %} {% set pmin = state_attr("sensor.shf_electricity_price_now", "today_min") | float %} {% set pmax = state_attr("sensor.shf_electricity_price_now", "today_max") | float %} {% set value = max(min((2*(pmax - pnow) / (pmax - pmin)-1) * states('input_number.shf_control_function_factor') | float,1),-1) %} {% if states("input_select.shf_control_function" ) == "Linear" %} {% set output.value = output.value + [value | round(2)] %} {% else %} {% set output.value = output.value + [sin(value*pi/2) | round(2)] %} {% endif %} {% endfor %} {{ output.value }}) renders=2>
Error while processing template: Template<template=({% set value = state_attr("sensor.shf_electricity_price_now", "data")[states("sensor.shf_idx")|int+now().hour] %} {{ value["Rank"] <= states("input_number.shf_rank_slider") | int }}) renders=2>
Error while processing template: Template<template=({% set value = state_attr("sensor.shf_electricity_price_now", "today_prices")[ now().hour ] %} {{ value <= states("input_number.shf_price_slider") | float }}) renders=2>
Traceback (most recent call last):
File "/usr/src/homeassistant/homeassistant/helpers/template.py", line 563, in async_render
render_result = _render_with_context(self.template, compiled, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/helpers/template.py", line 2179, in _render_with_context
return template.render(**kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/jinja2/environment.py", line 1301, in render
self.environment.handle_exception()
File "/usr/local/lib/python3.11/site-packages/jinja2/environment.py", line 936, in handle_exception
raise rewrite_traceback_stack(source=source)
File "<template>", line 1, in top-level template code
File "/usr/local/lib/python3.11/site-packages/jinja2/sandbox.py", line 303, in getitem
return obj[argument]
~~~^^^^^^^^^^
jinja2.exceptions.UndefinedError: None has no element 0

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
File "/usr/src/homeassistant/homeassistant/helpers/template.py", line 684, in async_render_to_info
render_info._result = self.async_render(variables, strict=strict, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/helpers/template.py", line 565, in async_render
raise TemplateError(err) from err
homeassistant.exceptions.TemplateError: UndefinedError: None has no element 0
 

juu-zo

Aktiivinen jäsen
Koitin muokkailla tuota Rank & Price skriptiä nyt haluamaani muotoon, joten voisiko joku varmistaa onko tämä nyt sen kaltainen kuin kuuluisi eli:
// Settings - Rank and price limit - rule 1, Rele kytkeytyy päälle vuorokauden halvimpina hintoina, joista priorisoidaan yösähkön aikaiset hinnat erotuksella -2,56c. Eli rank muuttuu sen mukaan jos yöllä hinta on alle tuon erotuksen verran kalliimpaa. (yösiirto siis tuon verran halvempaa). Tämä toiminto siis toimii ma-la päivinä.
// Settings - Rank and price limit - rule 2, Sunnuntaille sitten käytössä oma ehtonsa jossa katsotaan vain vuorokauden halvimpia hintoja ilman prioriteettejä.

// More information about the API (in Finnish): https://spot-hinta.fi/
// Support Shelly script and Spot-hinta.fi API development and maintenance: https://www.buymeacoffee.com/spothintafi

// This script can control up to four different relays:
// - Max two relays with PRICE limit only
// - Max two relays with RANK (number of cheapest hours) AND PRICE limit

// Region to use. See supported regions in Swagger documentation: https://api.spot-hinta.fi/swagger/ui
let Region = "FI";

// Settings - Price limit - rule 1
let SETTINGS_PRICELIMIT_1 =
{
// User settings
RelayIsInUse: false, // True/false: If you want to use this relay or not
PriceAllowed: "30", // Price limit (in euro cents, without decimals). Use "-99" if not wanted. If price is now less than this the relay is turned ON (or OFF if inverted - see below)
AllowedDays: "1,2,3,4,5,6,7", // Execution days: 1=Monday to 7=Sunday, separated with comma.
Relay: "0", // Shelly's relay number (Value is between 0-3 depending how many relays Shelly has). Make sure this is correct!
RelayName: "OilBoiler", // Name this relay. Name is used in debug log mostly.
Inverted: false, // True/false: Set to "true" to inverted the relay logic

// Script technical fields. Do not edit!
Url: 1,
SettingsNumber: 1,
RelayStatus: true,
RelayStatusSource: "",
RelayExecuted: false,
};

// Settings - Price limit - rule 2
let SETTINGS_PRICELIMIT_2 =
{
// User settings
RelayIsInUse: false,
PriceAllowed: "20",
AllowedDays: "1,2,3,4,5,6,7",
Relay: "0",
RelayName: "Charger",
Inverted: false,

// Script technical fields. Do not edit!
Url: 1,
SettingsNumber: 2,
RelayStatus: true,
RelayStatusSource: "",
RelayExecuted: false,
};

// Settings - Rank and price limit - rule 1
let SETTINGS_RANK_PRICE_1 =
{
// User settings
RelayIsInUse: true,
Rank: "5", // "Rank" limit (number of cheapest hours today)
PriceAllowed: "0",
MaxPrice: "999", // Maximum allowed price in euro cents.
AllowedDays: "1,2,3,4,5,6",
BackupHours: ["02", "03", "04", "05", "06"], // Backup hours; if API is not answering or internet connection is down.
BoosterHours: "99,99", // Relay is always ON during booster hours. Use "99,99" to disable this.
PriorityHours: ["22", "23", "00", "01", "02", "03", "04", "05", "06"], // Hours you want to prioritize. If PriceModifier: is "0" these hours always get the smallest 'rank'. Use "99,99" to disable this.
PriorityHoursRank: "5", // How many priority hours are prioritized. i.e. "3" = 3 cheapest priority hours.
PriceModifier: "-2,56", // If priority hours have lower price - such as 'night electricity' - the difference in Euro cents.
Relay: "0",
RelayName: "WaterHeater",
Inverted: false,

// Script technical fields. Do not edit!
Url: 2,
SettingsNumber: 3,
RelayStatus: true,
RelayStatusSource: "",
RelayExecuted: false,
};

// Settings - Rank and price limit - rule 2
let SETTINGS_RANK_PRICE_2 =
{
// User settings
RelayIsInUse: true,
Rank: "5",
PriceAllowed: "0",
MaxPrice: "999",
AllowedDays: "7",
BackupHours: ["02", "03", "04", "05", "06"],
BoosterHours: "99,99",
PriorityHours: "99,99",
PriorityHoursRank: "5",
PriceModifier: "-2,56",
Relay: "0",
RelayName: "WaterHeater sunday",
Inverted: false,

// Script technical fields. Do not edit!
Url: 2,
SettingsNumber: 4,
RelayStatus: true,
RelayStatusSource: "",
RelayExecuted: false,
};

// ------------------------------------
// Script - no need to modify (usually)
// ------------------------------------

// Variables needed to control the execution
let currentHour = "";
let currentHourUpdated = "";
let rounds = 0;

// This is triggered by the timer (see end of the script)
function ExecuteRelayRules() {

// Counter of exeutution rounds. First round means mostly.
rounds = rounds + 1;

// Update current hour in a global variable.
UpdateCurrentHour(rounds);

// Reset relays if hour has changed
InitializeRelaysIfHourHasChanged(rounds);

// This was initialization round. Next round is the first actual processing round.
if (rounds === 1) { print("Script initialization done."); return; }

// Return if current hour is already done.
if (HasCurrentHourBeenDone() === true) {
return;
}

// Execute Relays which are in use and not executed yet.
ExecuteRelayRule(SETTINGS_PRICELIMIT_1);
ExecuteRelayRule(SETTINGS_PRICELIMIT_2);
ExecuteRelayRule(SETTINGS_RANK_PRICE_1);
ExecuteRelayRule(SETTINGS_RANK_PRICE_2);
}

// Executes HTTP GET query for a given settings
function ExecuteRelayRule(Settings) {

// Do nothing if relay is not in use or it is already executed for this hour
if (Settings.RelayIsInUse === false || Settings.RelayExecuted === true) { return; }

// Get URL
let urlToCall = BuildUrl(Settings);

// HTTP Get Request to API
print("Execute HTTP GET. Relay name: " + Settings.RelayName + " - URL: " + urlToCall);
Shelly.call("HTTP.Request", { method: "GET", url: urlToCall, timeout: 10, ssl_ca: "*" }, ProcessHttpRequestResponse, Settings);
}

// Process response for HTTP GET
function ProcessHttpRequestResponse(response, error_code, error_msg, Settings) {

// Process response and get status if processing was done successfully.
let response = SetRelayStatusInShellyBasedOnHttpStatus(response, error_code, error_msg, Settings);

// Update relay executed status
if (Settings.SettingsNumber === 1) { SETTINGS_PRICELIMIT_1.RelayExecuted = response; }
if (Settings.SettingsNumber === 2) { SETTINGS_PRICELIMIT_2.RelayExecuted = response; }
if (Settings.SettingsNumber === 3) { SETTINGS_RANK_PRICE_1.RelayExecuted = response; }
if (Settings.SettingsNumber === 4) { SETTINGS_RANK_PRICE_2.RelayExecuted = response; }
}

// Control the relays based on the result from the API call
function SetRelayStatusInShellyBasedOnHttpStatus(response, error_code, error_msg, Settings) {

// Check for network errors
if (error_code !== 0 || response === null) {
print("Network error occurred: " + error_msg);
RunBackupHourRule(Settings);
return false;
}

if (response.code === 200) {
SetRelayStatusInShelly(Settings, true, "api"); // Relay is turned on.
return true;
}
else if (response.code === 400) {
SetRelayStatusInShelly(Settings, false, "api"); // Relay is turned off.
return true;
}
else {
print("HTTP status code does not indicate success. HTTP status code: " + JSON.stringify(response.code));
RunBackupHourRule(Settings);
return false;
}
}

// Change relay status in Shelly
function SetRelayStatusInShelly(Settings, newStatus, relayStatusSource) {

// Set status messages
let statusMessage = "";
if (newStatus === true && Settings.Inverted === true) { statusMessage = "Turn relay off (inverted). Relay name: " + Settings.RelayName; }
if (newStatus === false && Settings.Inverted === true) { statusMessage = "Turn relay on (inverted). Relay name: " + Settings.RelayName; }
if (newStatus === true && Settings.Inverted === false) { statusMessage = "Turn relay on. Relay name: " + Settings.RelayName; }
if (newStatus === false && Settings.Inverted === false) { statusMessage = "Turn relay off. Relay name: " + Settings.RelayName; }

// Switch behavior based on Inverted-setting
if (Settings.Inverted === true && newStatus === true) { newStatus = false; }
else if (Settings.Inverted === true && newStatus === false) { newStatus = true; }

// Don't close relay if it is already registered as closed AND source in last control is 'api'
// When closing has been done by 'backupHour', relay can be closed again.
if (Settings.RelayStatus === false && newStatus === false && Settings.RelayStatusSource === "api") {
print("Relay is already closed. Not closing again (this allows possible other script to act)");
return;
}

// Set relay in Shelly
print(statusMessage);
Shelly.call("Switch.Set", "{ id:" + Settings.Relay + ", on:" + JSON.stringify(newStatus) + "}", null, null);

// Save status information into settings
SetRelayStatusInSettings(Settings, newStatus, relayStatusSource);
}

// Set relay status for a given settings
function SetRelayStatusInSettings(Settings, newStatus, relayStatusSource) {

if (Settings.SettingsNumber === 1) { SETTINGS_PRICELIMIT_1.RelayStatus = newStatus; SETTINGS_PRICELIMIT_1.RelayStatusSource = relayStatusSource; }
if (Settings.SettingsNumber === 2) { SETTINGS_PRICELIMIT_2.RelayStatus = newStatus; SETTINGS_PRICELIMIT_2.RelayStatusSource = relayStatusSource; }
if (Settings.SettingsNumber === 3) { SETTINGS_RANK_PRICE_1.RelayStatus = newStatus; SETTINGS_RANK_PRICE_1.RelayStatusSource = relayStatusSource; }
if (Settings.SettingsNumber === 4) { SETTINGS_RANK_PRICE_2.RelayStatus = newStatus; SETTINGS_RANK_PRICE_2.RelayStatusSource = relayStatusSource; }
}

// This is executed if API did not respond properly. This is NOT considered as successful execution
function RunBackupHourRule(Settings) {

// Check if settings has BackupHours property
let hasBackupRules = false;
for (let property in Settings) {
if (property === "BackupHours") { hasBackupRules = true; }
}

// Return if not configured
if (hasBackupRules === false) { print("This setting does not have BackupHours property. Relay name: " + Settings.RelayName); return; }

// Check if current hour is listed in backup hours
let currentHourIsListed = false;
for (let i = 0; i < Settings.BackupHours.length; i++) {
if (Settings.BackupHours === currentHour) { currentHourIsListed = true; }
}

// When current hour has not been updated for some reason, it might be better to consider this as backup hour
// Comment this line, if you don't like the behavior.
if (currentHour === null || currentHour === "") { currentHourIsListed = true; }

// Set Shelly relay according if hour is backup hour.
print("Executing backup rule for relay: " + Settings.RelayName + " - Current hour: " + currentHour + " - Current hour is backup hour: " + JSON.stringify(currentHourIsListed));
SetRelayStatusInShelly(Settings, currentHourIsListed, "backupHour");
}

// Get the current hour and put it in global variable
function UpdateCurrentHour(rounds) {

Shelly.call("Shelly.GetStatus", "", function (res, rounds) {
if (res.sys.time !== null) {
currentHourUpdated = res.sys.time.slice(0, 2); // f.ex. "21:34"
if (rounds === 1) { currentHour = currentHourUpdated; }
}
else {
currentHourUpdated = ""; // Time is null if Shelly does not have connection to time server
}
}, rounds);
}

// Initialize relay statuses if hour has changed
function InitializeRelaysIfHourHasChanged(rounds) {

if (currentHour !== currentHourUpdated || rounds === 1) {
// Update current hour
currentHour = currentHourUpdated;

// Skip relays which are not in use - set their "RelayExecuted" state to true.
if (SETTINGS_PRICELIMIT_1.RelayIsInUse === true) { SETTINGS_PRICELIMIT_1.RelayExecuted = false } else { SETTINGS_PRICELIMIT_1.RelayExecuted = true; };
if (SETTINGS_PRICELIMIT_2.RelayIsInUse === true) { SETTINGS_PRICELIMIT_2.RelayExecuted = false } else { SETTINGS_PRICELIMIT_2.RelayExecuted = true; };
if (SETTINGS_RANK_PRICE_1.RelayIsInUse === true) { SETTINGS_RANK_PRICE_1.RelayExecuted = false } else { SETTINGS_RANK_PRICE_1.RelayExecuted = true; };
if (SETTINGS_RANK_PRICE_2.RelayIsInUse === true) { SETTINGS_RANK_PRICE_2.RelayExecuted = false } else { SETTINGS_RANK_PRICE_2.RelayExecuted = true; };
}
}

// Check if all relays are done for this hour.
function HasCurrentHourBeenDone() {

if (SETTINGS_PRICELIMIT_1.RelayExecuted === true &&
SETTINGS_PRICELIMIT_2.RelayExecuted === true &&
SETTINGS_RANK_PRICE_1.RelayExecuted === true &&
SETTINGS_RANK_PRICE_2.RelayExecuted === true) {
print("Current hour is already done.");
return true;
}

return false;
}

// Builds URL to call the API
function BuildUrl(Settings) {

// Price limit URL
if (Settings.Url === 1) {
return "https://api.spot-hinta.fi/JustNow/" + Settings.PriceAllowed + "?region=" + Region + "&allowedDays=" + Settings.AllowedDays;
}

// Price and Rank limit URL
if (Settings.Url === 2) {
let url = "https://api.spot-hinta.fi/JustNowRank/" + Settings.Rank + "/" + Settings.PriceAllowed;
url += "?maxPrice=" + Settings.MaxPrice;
url += "&allowedDays=" + Settings.AllowedDays;
url += "&boosterHours=" + Settings.BoosterHours;
url += "&priorityHours=" + Settings.PriorityHours;
url += "&priorityHoursRank=" + Settings.PriorityHoursRank;
url += "&priceModifier=" + Settings.PriceModifier;
url += "&region=" + Region;
return url;
}

return "";
}

// Main timer to execute rules
Timer.set(30000, true, ExecuteRelayRules);
 

juu-zo

Aktiivinen jäsen
Laitoin skriptin pyörimään shellylle, mutta scheduleen ei ainakaan tule aikataulua päälle kytkeytymiseen. Konsoliin tulee ainoastaan "current hour is already done" teksti vähän väliä. Miten tuota nyt lähtisi enemmän debuggaamaan että missä on häikkää?

Edit. Tuo ei näköjään mitään schedulea luokaan vaan katsoo siis jatkuvasti onko kyseisen tunnin kysely jo tehty ja mikä status sillä on. Eli näyttäisi toimivan. Pitänee jollain koittaa logata tuota että toimiiko tuo nyt oikein sekä viikonpäivinä että sunnuntaisin kun niihin on erikseen nuo skriptit.
 
Viimeksi muokattu:

Mikki

Hyperaktiivi
Laitoin skriptin pyörimään shellylle, mutta scheduleen ei ainakaan tule aikataulua päälle kytkeytymiseen. Konsoliin tulee ainoastaan "current hour is already done" teksti vähän väliä. Miten tuota nyt lähtisi enemmän debuggaamaan että missä on häikkää?

Edit. Tuo ei näköjään mitään schedulea luokaan vaan katsoo siis jatkuvasti onko kyseisen tunnin kysely jo tehty ja mikä status sillä on. Eli näyttäisi toimivan. Pitänee jollain koittaa logata tuota että toimiiko tuo nyt oikein sekä viikonpäivinä että sunnuntaisin kun niihin on erikseen nuo skriptit.
Historiaan kertyy Shellyssä että miten relettä on kytketty päälle ja pois. Skriptit eicät tosiaan käytä scheduleria, vaan kerran tunnissa kyselevät mitä seuraavaksi tehdään.

Tuo lokitus "current hour"... Kertoo että kuluvan tunnin kysely on tehty onnistuneesti.
 

juu-zo

Aktiivinen jäsen
Historiaan kertyy Shellyssä että miten relettä on kytketty päälle ja pois. Skriptit eicät tosiaan käytä scheduleria, vaan kerran tunnissa kyselevät mitä seuraavaksi tehdään.

Tuo lokitus "current hour"... Kertoo että kuluvan tunnin kysely on tehty onnistuneesti.
Mistä tuon historian näkee? En mielestäni löydä mitään historia logia tuolta shellystä. Muutoin tosiaan skripti tuntuu toimivan ihan hyvin. Kasvatin tuota hakuaikaa vähän ettei kyselyjä tehdä 30sec välein.
 

Mikki

Hyperaktiivi
Mistä tuon historian näkee? En mielestäni löydä mitään historia logia tuolta shellystä. Muutoin tosiaan skripti tuntuu toimivan ihan hyvin. Kasvatin tuota hakuaikaa vähän ettei kyselyjä tehdä 30sec välein.
Releen tietojen takaa sen pitäisi näkyä. Tuo skripti ei tee serverihakua 30sec välein, vaan tarkistaa lähinnä kellonajan että onko tunti vaihtunut. Mutta ei loopin harvennuksesta haittaa ole suuremmin, lähinnä tunnin vaihtuminen havaitaan keskimäärin hitaammin.
 

juu-zo

Aktiivinen jäsen
Releen tietojen takaa sen pitäisi näkyä. Tuo skripti ei tee serverihakua 30sec välein, vaan tarkistaa lähinnä kellonajan että onko tunti vaihtunut. Mutta ei loopin harvennuksesta haittaa ole suuremmin, lähinnä tunnin vaihtuminen havaitaan keskimäärin hitaammin.
Niinpä onkin eli tunti tarkastetaan useammin, mutta serverihaku tehdään vain kun uusi tunti on vaihtunut. Releen tietojen alla ei näy kuin viimeisin muutos mikä on tehty, mutta ei pidempää historiaa. Ehkä nykyisessä firmiksessä ei tuollaista historiatietoa ole.
 

Samuvaan

Tulokas
Hei. Haluaisin apua Shelly -releen valintaan. Meillä on sähkökeskuksessa neljä kontaktoria (3xlattialämmitys 1xLVV) mitä haluaisin ohjata Shellyllä ja Mikin scriptillä. En löytänyt tuolta http://spot-hinta.fi/shelly-skriptien-esittely/ ainakaan valmiita scriptiä Shelly Pro 4PM:ään ja itsellä ei riitä taito muuttaa koodia mitenkään. Sähkökytkennät sähköasentajana kuitenkin pitäisi onnistua. Energia mittausta en tule tarvitsemaan kun tarkoituksena on vain ohjata lämmityksen kontaktoreita. Pitääkö minun siis ostaa neljä erillistä Shelly Pro:ta?
 

mobbe

Vakionaama
Hei. Haluaisin apua Shelly -releen valintaan. Meillä on sähkökeskuksessa neljä kontaktoria (3xlattialämmitys 1xLVV) mitä haluaisin ohjata Shellyllä ja Mikin scriptillä. En löytänyt tuolta http://spot-hinta.fi/shelly-skriptien-esittely/ ainakaan valmiita scriptiä Shelly Pro 4PM:ään ja itsellä ei riitä taito muuttaa koodia mitenkään. Sähkökytkennät sähköasentajana kuitenkin pitäisi onnistua. Energia mittausta en tule tarvitsemaan kun tarkoituksena on vain ohjata lämmityksen kontaktoreita. Pitääkö minun siis ostaa neljä erillistä Shelly Pro:ta?
Tuon pitäisi soveltua https://github.com/Spot-hinta-fi/Shelly/blob/main/Scripts/Shelly-Rank_and_Price_limit.js
oletus kaikissa on relay 0 mutta käytettävissä vaihtoehdot 0-3 eli neljää lähtöä ja kontaktorin ohjausta.
 

Mikki

Hyperaktiivi
Hei. Haluaisin apua Shelly -releen valintaan. Meillä on sähkökeskuksessa neljä kontaktoria (3xlattialämmitys 1xLVV) mitä haluaisin ohjata Shellyllä ja Mikin scriptillä. En löytänyt tuolta http://spot-hinta.fi/shelly-skriptien-esittely/ ainakaan valmiita scriptiä Shelly Pro 4PM:ään ja itsellä ei riitä taito muuttaa koodia mitenkään. Sähkökytkennät sähköasentajana kuitenkin pitäisi onnistua. Energia mittausta en tule tarvitsemaan kun tarkoituksena on vain ohjata lämmityksen kontaktoreita. Pitääkö minun siis ostaa neljä erillistä Shelly Pro:ta?

Ehdottaisin melkein ettö ostat yhden Shelly Pro 1 -releen LVV kontaktorille ja sitten Shelly Pro 3 lämmityksen ohjaukseen. Ei kannata ostaa "PM" mallia jos ei energiamittausta käytä. Ja toisekseen ei kaikkea ohjausta yhden laitteen varaan.

LVV kontaktoriksi tämä Hager on hyvä. Vaihda kontaktori samalla jos on vanha kontaktori -- vanhat kontaktorit antaa herkästi takapotkuja kun relettä kytketään: https://www.onninen.fi/hager-kontaktori-hager-etc325s-3s-25a-k-230v-lvv-hur/p/AME614

Ja kuten yllä mainittiin voi eri releitä ohjata kun vaihtaa skriptissä releen numeroa. 0,1,2 on esim. Shelly Pro 3 releiden numerot.
 
Viimeksi muokattu:

-Teme-

Aktiivinen jäsen
Tämäntapaisillakin ohjailee hyvin LVV 1kW lämmitysvastuksia. Käsikäyttöä ei ole mutta saapahan jokaisen vaiheen erikseen ohjattua. Ohjausjännitteitä löytyy ainkain DC12, DC24 ja AC24 ja AC230.
https://www.eibabo.fi/finder/kytken...1.9.012.0050-eb10745566?fs=1090418435&c=99521
Vastaavasti voi käyttää 2kpl Hagerin ESC225 kontaktoria. Toiseen yksi vastus ja toiseen kaksi. Tällöin vuorottelemalla tai yhdessä saa kuormaksi 1-2-3 vastusta
Hagerit onnisella n.26€/kpl riippuen vähän sopparista
 

-Teme-

Aktiivinen jäsen
Ehkä pykälää parempi edelliseen 230Vac vaihtoehtoon on ERC225S joka häiriösuojattu hurinaton, sekä sisältää käsiohjauksen mutta hinta vain pari euroa enemmän.
24Vac ohjaukseen ERD225S joka suunnilleen samanhintainen kuin 230Vac rinnakkaismalli ja 24Vdc ohjaukseen ERD225SDC joka n.30€
 

Mikki

Hyperaktiivi
Ehkä pykälää parempi edelliseen 230Vac vaihtoehtoon on ERC225S joka häiriösuojattu hurinaton, sekä sisältää käsiohjauksen mutta hinta vain pari euroa enemmän.
24Vac ohjaukseen ERD225S joka suunnilleen samanhintainen kuin 230Vac rinnakkaismalli ja 24Vdc ohjaukseen ERD225SDC joka n.30€
Joo... älkää ihmeessä säästäkö muutamaa euroa näissä, vaan ostakaa kunnollinen. Se hurinaton missä on vipu käsiohjaukselle on erinomainen ratkaisu sen varalta että Shelly tai muu ohjauslaite sanoo itsensä irti.
 

juu-zo

Aktiivinen jäsen
@Mikki toimiiko pricelimit skriptin kanssa negatiiviset arvot? Tai lähinnä jos price allowed hinnaksi laittaa 0 niin antaako aina 200 vastauksen kun hinta putoaa negatiiviseksi? Pikaisesti testaamalla ainakaan ei anna mitään erroria vaikka hinnan pudottaisi -1 jolloin tuon pitäisi sallia rele vasta -1c hinnassa.
Lisänä onko kokemusta mitä tapahtuu rank&price skriptissä jossa käytetään samaa relettä useammalla eri ehdolla samana päivänä. Eli esimerkkinä price skriptillä sallitaan aina alle 0c hinnat releelle ja samalla pyöritetään rank skriptiä niin että se sallii vain 4h päivän alimmilla tunneilla lämmittämistä. Yliajaako jompi kumpi skripti toisen vai miten tuossa tapauksessa kyselyt ja komennot tehdään eli voiko tuosta aiheutua turhaa kontaktorin paukuttelua suuntaan tai toiseen?
 

Mikki

Hyperaktiivi
@Mikki toimiiko pricelimit skriptin kanssa negatiiviset arvot? Tai lähinnä jos price allowed hinnaksi laittaa 0 niin antaako aina 200 vastauksen kun hinta putoaa negatiiviseksi? Pikaisesti testaamalla ainakaan ei anna mitään erroria vaikka hinnan pudottaisi -1 jolloin tuon pitäisi sallia rele vasta -1c hinnassa.
Lisänä onko kokemusta mitä tapahtuu rank&price skriptissä jossa käytetään samaa relettä useammalla eri ehdolla samana päivänä. Eli esimerkkinä price skriptillä sallitaan aina alle 0c hinnat releelle ja samalla pyöritetään rank skriptiä niin että se sallii vain 4h päivän alimmilla tunneilla lämmittämistä. Yliajaako jompi kumpi skripti toisen vai miten tuossa tapauksessa kyselyt ja komennot tehdään eli voiko tuosta aiheutua turhaa kontaktorin paukuttelua suuntaan tai toiseen?

Kyllä negatiivinen raja pitäisi toimia mitä nopeasti koodia vilkaisin (ihme ongelmiin on päädytty viime talven paniikkihinnoista :) ).

Ei kannata ajaa samaa relettä kohden useita eri sääntöjä (saman päivän aikana). Näin tehden voi tulla tosiaan päällekkäisiä ohjauksia jotka menevät ristiin. Hintarajan + rank rajan saa yhteenkin sääntöön, että tuolle ei pitäisi olla tarvetta.
 
Viimeksi muokattu:

juu-zo

Aktiivinen jäsen
Kyllä negatiivinen raja pitäisi toimia mitä nopeasti koodia vilkaisin (ihme ongelmiin on päädytty viime talven paniikkihinnoista :) ).

Ei kannata ajaa samaa relettä kohden useita eri sääntöjä. Näin tehden voi tulla tosiaan päällekkäisiä ohjauksia jotka menevät ristiin. Hintarajan + rank rajan saa yhteenkin sääntöön, että tuolle ei pitäisi olla tarvetta.
Tätä vähän itsekin tuossa tutkailin ja sitten tutkinkin vielä tarkemmin että tosiaan minimihinnan saa suoraan rank&price skriptiinkin asetettua, joten ei ongelmaa tehdä haluttua toimintoa. Ideana että aina alle 0 hinnoilla LVV on päällä, ma-pe priorisoidaan klo 22-07 -2,74 price modifierillä (yösiirto) ja sunnuntaisin katsotaan vain normaalisti 4 alinta tuntia ilman muuttujia. Kuitenkin molempia rank&price skriptejä voi ilmeisesti käyttään näin että toinen on sallittu päivinä 1,2,3,4,5,6 ja toinen päivänä 7?

Ei tosiaan varmaan viime kesänä tullut mieleen että tarvitsee miettiä negatiivisia hintoja, mutta aurinkopaneelitaloudessa mielellään yrittää kaiken mahdollisen tuoton käyttää itse pitämällä LVV päällä koko ajan jos hinta putoaa negatiiviseksi.

Edit. Tuli muuten vielä mieleen että onko api.spot-hinta.fi rajapinnalle olemassa jotain uutiskirjettä tai vastaavaa joka ilmottaisi muutoksista palveluun/skripteihin tai muihin?
 
Viimeksi muokattu:

Mikki

Hyperaktiivi
Kuitenkin molempia rank&price skriptejä voi ilmeisesti käyttään näin että toinen on sallittu päivinä 1,2,3,4,5,6 ja toinen päivänä 7?

....

Edit. Tuli muuten vielä mieleen että onko api.spot-hinta.fi rajapinnalle olemassa jotain uutiskirjettä tai vastaavaa joka ilmottaisi muutoksista palveluun/skripteihin tai muihin?

Joo voi käyttää noin skriptejä. Ja muutoksista pyrin ilmoittelemaan kyllä tässä ketjussa, mutta ehkä "X:ssä" eli Twitterissä olisi se ns. virallisempi tiedotuskanava: https://twitter.com/Spot_hinta_fi (pitikin heti käydä laittamassa yksi muutostiedote sinne).
 
Viimeksi muokattu:

juu-zo

Aktiivinen jäsen
AllowedMonths on hyvä lisä rajapintaan niin nyt pystyy kausisähköläiset hanskaamaan yhdellä skriptillä koko vuoden eikä tarvitse vaihtaa eri skriptien välillä.
 

juu-zo

Aktiivinen jäsen
@Mikki
Rank&price skripti tuntuisi antavan jotain erroria shellyn pulesta jos on enemmän kuin 2 sääntöä aktiivisena. ”Uncaught error: too many calls in progress” eli ilmeisesti ei pysty hanskaamaan noita http requesteja. Tämä siis ainakin shelly plus 1 värkillä.
 

Mikki

Hyperaktiivi
@Mikki
Rank&price skripti tuntuisi antavan jotain erroria shellyn pulesta jos on enemmän kuin 2 sääntöä aktiivisena. ”Uncaught error: too many calls in progress” eli ilmeisesti ei pysty hanskaamaan noita http requesteja. Tämä siis ainakin shelly plus 1 värkillä.
Tuolla mJs puolella on kyllä aika tiukkoja rajoituksia moneen asiaan. Muistan joskus kanssa nähneeni tuon virheen, mutta en ole ihan päässyt kartalle, että mitkä lasketaan requesteiksi.

Periaatteessa nuo requestit tehdään peräkkäin, ettei niitä pitäisi olla montaa ajossa kerralla. Mutta voin kokeilla saisinko toistumaan tuon.
 

kayttajatunnus

Aktiivinen jäsen
Itsellä tosiaan lopetti rank&price skripti toiminnan kun asetin samalle releelle eri ehdot eri päiville JA laitoin invertin trueksi. Ennen invertiä mielestäni toimi hyvin.

Pitäisi testata tämä vielä uudelleen.
 

tk-

Aktiivinen jäsen
Tätä vähän itsekin tuossa tutkailin ja sitten tutkinkin vielä tarkemmin että tosiaan minimihinnan saa suoraan rank&price skriptiinkin asetettua, joten ei ongelmaa tehdä haluttua toimintoa. Ideana että aina alle 0 hinnoilla LVV on päällä, ma-pe priorisoidaan klo 22-07 -2,74 price modifierillä (yösiirto) ja sunnuntaisin katsotaan vain normaalisti 4 alinta tuntia ilman muuttujia. Kuitenkin molempia rank&price skriptejä voi ilmeisesti käyttään näin että toinen on sallittu päivinä 1,2,3,4,5,6 ja toinen päivänä 7?

Ei tosiaan varmaan viime kesänä tullut mieleen että tarvitsee miettiä negatiivisia hintoja, mutta aurinkopaneelitaloudessa mielellään yrittää kaiken mahdollisen tuoton käyttää itse pitämällä LVV päällä koko ajan jos hinta putoaa negatiiviseksi.

Edit. Tuli muuten vielä mieleen että onko api.spot-hinta.fi rajapinnalle olemassa jotain uutiskirjettä tai vastaavaa joka ilmottaisi muutoksista palveluun/skripteihin tai muihin?
Mielenkiinnosta, miksi sunnuntaina on eri logiikka kuin muina päivinä? Kausisähkö käytössä?
 

juu-zo

Aktiivinen jäsen
Mielenkiinnosta, miksi sunnuntaina on eri logiikka kuin muina päivinä? Kausisähkö käytössä?
Kyllä, siirtotuote on kausisähkö ja sillä sunnuntai on muu aika-sähköä. Käytännössä kolmella sääntö kombolla pärjäisi koko vuoden. Talvi ma-la sääntö, talvi su sääntö ja kesä ma-su sääntö.
 

Mikki

Hyperaktiivi
Kyllä, siirtotuote on kausisähkö ja sillä sunnuntai on muu aika-sähköä. Käytännössä kolmella sääntö kombolla pärjäisi koko vuoden. Talvi ma-la sääntö, talvi su sääntö ja kesä ma-su sääntö.
Asenna sama skripti toiseen kertaan ja pistä kesän sääntö toiseen ja talven säännlt toiseen. Ajossa voi olla max. kolme skriptiä yhtäaikaa.

Luulen että rajoitukset ovat skriptikohtaisia. Ehkä. Tässä voi olla tapahtunut jotain muutosta firmware päivityksissä.
 

tk-

Aktiivinen jäsen
Eikös tämä ole vieläpä niin, että sunnuntaisin ja pyhäpäivinä?
Muistaakseni tätä selvittelin aikanaan, ja arkipyhät menee arkihinnalla. Toivottavasti se on näin, tai muuten tulee aika paljon lisätyötä tuohon muutaman viikon sisään julkaistavaan seuraavaan Pörssäri-versioon mikä osaa ottaa myös kausisähkön huomioon..
 
Joo voi käyttää noin skriptejä. Ja muutoksista pyrin ilmoittelemaan kyllä tässä ketjussa, mutta ehkä "X:ssä" eli Twitterissä olisi se ns. virallisempi tiedotuskanava: https://twitter.com/Spot_hinta_fi (pitikin heti käydä laittamassa yksi muutostiedote sinne).
Harmi että tuonne pitää olla nykyään rekisteröitynyt, että pääsee lukemaan tuolla olevia juttuja.

Mutta mikä muu alusta se voisi olla, niin siihen minulla ei ole mitään antaa :) Saahan tuonne ilmeisesti ilmaiseksi rekisteröityä jos haluaa..?
 

Mikki

Hyperaktiivi
Harmi että tuonne pitää olla nykyään rekisteröitynyt, että pääsee lukemaan tuolla olevia juttuja.

Mutta mikä muu alusta se voisi olla, niin siihen minulla ei ole mitään antaa :) Saahan tuonne ilmeisesti ilmaiseksi rekisteröityä jos haluaa..?

Joo kyllä sinne voi rekisteröityä ilmaiseksi. Vaikka Elon Musk säätää aika isosti ko. palvelun kanssa, niin kyllä se vielä toistaiseksi ainakin on parhaasta päästä jakelukanavia informaatiolle.

Mutta tässä ketjussa yritän ainakin pääosin kertoa kyllä mitä isompia muutoksia tai korjauksia on rajapintoihin ja Shelly skripteihin tehty.
Tuo viimeisin viesti X:ään (Twitteriin) meni näin:

Rajapintoihin, joissa on "allowedDays" parametri, on nyt lisätty "allowedMonths" parametri. Halutessa voi siis tehdä vuodenajan mukaan eri sääntöjä. https://api.spot-hinta.fi/swagger/ui#/
Tuki on tulossa Shelly skripteihin myös lähiaikoina. Testiversiot ovat täällä: https://github.com/Spot-hinta-fi/Shelly/tree/allowedMonths
 
Back
Ylös Bottom