Jump to content

Simple weather script with virtual components


ViX

Recommended Posts

image.png.27d73ca0c3d4e4357e4bb1ed1c848dc1.png

Script uses openweathermap data so you must get API key for this to work.

There's quite many supported languages. Just change lang=fi to your needs. Line

let url = CONFIG.weatherURL + "?appid=" + CONFIG.OpenWeatherAPIkey + "&lat=" + CONFIG.lat + "&lon=" + CONFIG.lon + "&units=metric&lang=fi";

 

I have virtual text components 201 and 202. Change those values to what you have.

 

 
let CONFIG = {
  OpenWeatherAPIkey: "GetYourOwnAPIkey",            // Sign up to openweathermap.org. And get your APIkey. 
  weatherURL: "https://api.openweathermap.org/data/2.5/weather",   // Don't touch

  Debug: false,                          // Prints out where script is going every 30secs.
  RebootEveryDay: false,                  // Reboots every day.
  
// Location
// Get your local the coordinates: https://www.latlong.net/

// Test location
  lat: 75.977, 
  lon: 64.96,



};

// Don't touch below!
print("Script has started. The first relay action in 30 seconds.");
let cHour = null;
let GetWeather = true;
let Executed = false;
let WeatherLastDate = 0;

function getWeatherURL() {
  let url = CONFIG.weatherURL + "?appid=" + CONFIG.OpenWeatherAPIkey + "&lat=" + CONFIG.lat + "&lon=" + CONFIG.lon + "&units=metric&lang=fi";
  if (CONFIG.Debug === true) {
    print("Weather URL: " + url);
  }
  return (url);
}

function padStart(num, targetLength, padString) {
  let str = num.toString();
  while (str.length < targetLength) {
    str = padString ? padString + str : " " + str;
  }
  return str;
}

// Function to update Shelly number virtual component
function updateTextComponent(component_id, value) {
//  print("Updating component ID: " + component_id + " with value: " + value);
  Shelly.call(
    "Text.Set",
    {
      "id": component_id,
      "value": value
    },
    function(result, error_code, error_message) {
      if (error_code !== 0) {
        print("Error updating text component: " + error_message);
      } else {
        if (CONFIG.Debug === true) {
          print("Text component updated. ID: " + component_id + ", Value: " + value);
        }
      }
    }
  );
} 

function setWeatherIcon(response, error_code, error_message) {
  if (error_code !== 0) {
// HTTP call to error service failed
// TODO: retry logic
    print("Weather data NOT succesfull");
    print("Error ", error_code );
    Error = true;
    GetWeather = false;
    let now = new Date();
    WeatherLastDate = new Date(now + (60*15*1000)); // Add 15min to date
    return;
  }
  if (response.code === 200) {  // Haku onnistui
    let weatherData = JSON.parse(response.body);
    response = null;   // Vapautetaan muistia;
    updateTextComponent(201, "https://openweathermap.org/img/wn/" + weatherData.weather[0].icon + "@2x.png")
    updateTextComponent(202, weatherData.main.temp.toFixed(0) + "°C   " + weatherData.weather[0].description)
    WeatherLastDate = new Date(weatherData.dt);
    WeatherLastDate = WeatherLastDate + (60*15); // Add 15min to date
    
    if (CONFIG.Debug === true) {
      print(weatherData.weather[0].icon);
      print(weatherData.main.temp.toFixed(0) + "°C   " + weatherData.weather[0].description)
      print(WeatherLastDate);
    }
    Executed = true;
  }
}

    
    
Timer.set(30000, true, function () {
    let hour = new Date().getHours();
    let minute = new Date().getMinutes();


    
// Clock turns 17 -> 18 get new data
    if (cHour != hour && hour == 18 && Executed == true) {
      if (CONFIG.Debug === true) {
		print("It's 18. Let's get new data.");
      }
	  cHour = null;
      Error = false;
      PricesLastDate = 0;
      priceData = null;
      GetWeather = true;
      Executed = false; 
      if (CONFIG.RebootEveryDay === true) {
        Shelly.call("Shelly.Reboot");
      }
    }



// Weather data is old. New data is neened.
    if ((WeatherLastDate * 1000) < Date.now()) {
      print(WeatherLastDate +"  "+ Date.now());
      if (CONFIG.Debug === true) {
        print("Weather data old. Now we need new weather data.");
      }
      Error = false;
      WeatherLastDate = 0;
      GetWeather = true;
    }

// This hour is already taken care of.
    else { 
      if (CONFIG.Debug === true) {
        print(padStart(hour, 2, "0") + ":" + padStart(minute, 2, "0")); 
      }
      return; 
    }


    // Get weather data
    if (GetWeather === true) {
      Shelly.call("HTTP.GET", { url: getWeatherURL() , timeout: 15, ssl_ca: "*" }, setWeatherIcon);
    }

});

 

Edited by ViX
Link to comment
Share on other sites

I had this API running on my Home Assistant last year and was happy with it.
Until at some point the number of queries (month) in the free package was limited and I switched to another weather API provider.

  • My question now would be which API package are you using to run this interesting script?
  • Is the free package suitable for long-term operation (months/years) with no cost?

image.png.34c79da38f414e1c79d1aef308b22ba0.png

Link to comment
Share on other sites

Hello

https://developer.yr.no/ or https://www.weatherapi.com/ Might be usable too.

Yr.no works fine, but it gives way too much info on each query for shelly. That amount of data might be just fine for Home Assistant.

BUT if you exceed 1000000 calls/month I think you should put some timers to slow calls down.

Link to comment
Share on other sites

 

image.png.fdbd8e585294e08d414399026acb021f.png

 

Made same code with AccuWeather. Accuweather have nicer icons 😀 But keep update interval quite slow because Accuweather gives just 50 polls per day (for free).

image.png.a515930dff90fa36d86856423dc4b63c.png

Get your location code, API key and set your language to fit your needs.

let CONFIG = {
  AccuWeatherAPIkey: "API",            // Sign up to accuweather.com. And get your APIkey. 
  weatherURL: "https://dataservice.accuweather.com/currentconditions/v1/",   // Don't touch

  Debug: false,                          // Prints out where script is going every 30secs.
  RebootEveryDay: false,                  // Reboots every day.
  
// Location
// Seinäjoki
  location: "134269",               // <-- Get this code from accuweather
  lang: "fi",                       // Set language
};

// Don't touch below!
print("Script has started. The first relay action in 30 seconds.");
let cHour = null;
let GetWeather = true;
let Executed = false;
let WeatherLastDate = 0;

function getWeatherURL() {
  // 
  let url = CONFIG.weatherURL + CONFIG.location + "?apikey=" + CONFIG.AccuWeatherAPIkey + "&language=" + CONFIG.lang + "&details=false";
  if (CONFIG.Debug === true) {
    print("Weather URL: " + url);
  }
  return (url);
}

function padStart(num, targetLength, padString) {
  let str = num.toString();
  while (str.length < targetLength) {
    str = padString ? padString + str : " " + str;
  }
  return str;
}

// Function to update Shelly number virtual component
function updateTextComponent(component_id, value) {
//  print("Updating component ID: " + component_id + " with value: " + value);
  Shelly.call(
    "Text.Set",
    {
      "id": component_id,
      "value": value
    },
    function(result, error_code, error_message) {
      if (error_code !== 0) {
        print("Error updating text component: " + error_message);
      } else {
        if (CONFIG.Debug === true) {
          print("Text component updated. ID: " + component_id + ", Value: " + value);
        }
      }
    }
  );
} 

function setWeatherIcon(response, error_code, error_message) {
  if (response.code !== 200 || error_code !== 0) {
// HTTP call to error service failed
// TODO: retry logic
    print("Weather data NOT succesfull");
    print("Error ", error_code);
    print("Response ", response.code);
    Error = true;
    GetWeather = false;
    let now = new Date();
    WeatherLastDate = new Date(now + (60 * 90 * 1000)); // Add 90min to date
    print(WeatherLastDate);
    updateTextComponent(202, "CODE " + response.code + " ERROR " + error_code);
    return;
  }
  if (response.code === 200) {  // Haku onnistui
    let weatherData = JSON.parse(response.body);
    response = null;   // Vapautetaan muistia;
    updateTextComponent(201, "https://www.accuweather.com/images/weathericons/" + weatherData[0].WeatherIcon + ".svg");
    updateTextComponent(202, weatherData[0].Temperature.Metric.Value.toFixed(0) + "°C   " + weatherData[0].WeatherText);
    WeatherLastDate = new Date(weatherData[0].EpochTime);
    WeatherLastDate = (WeatherLastDate + (60 * 65)) * 1000; // Add 65min to date

    if (CONFIG.Debug === true) {
      print(weatherData[0].EpochTime);
      print(WeatherLastDate / 1000);
      print(weatherData[0].WeatherIcon);
      print(weatherData[0].Temperature.Metric.Value.toFixed(0) + "°C");
      print(weatherData[0].WeatherText);
    }
    Executed = true;
    weatherData = null;   // Vapautetaan muistia;
  }
}

Timer.set(45000, true, function () {
    let hour = new Date().getHours();
    let minute = new Date().getMinutes();

// Clock turns 17 -> 18 get new data
    if (cHour !== hour && hour === 18 && Executed === true) {
      if (CONFIG.Debug === true) {
		print("It's 18. Let's get new data.");
      }
	  cHour = null;
      Error = false;
      PricesLastDate = 0;
      priceData = null;
      GetWeather = true;
      Executed = false; 
      if (CONFIG.RebootEveryDay === true) {
        Shelly.call("Shelly.Reboot");
      }
    }

// Weather data is old. New data is neened.
    if (WeatherLastDate < Date.now()) {
      if (CONFIG.Debug === true) {
        print(WeatherLastDate + "  " + Date.now());
        print("Weather data old. Now we need new weather data.");
      }
      Error = false;
      WeatherLastDate = 0;
      GetWeather = true;
      Executed = false; 
    }

// This hour is already taken care of.
    else { 
      if (CONFIG.Debug === true) {
        print(padStart(hour, 2, "0") + ":" + padStart(minute, 2, "0")); 
      }
      return; 
    }

    // Get weather data
    if (GetWeather === true) {
      Shelly.call("HTTP.GET", { url: getWeatherURL() , timeout: 15, ssl_ca: "*" }, setWeatherIcon);
    }
});

Make Text -> image

image.thumb.png.e98e35b3d225206c2b8da19759001237.png

And Text -> Label

image.thumb.png.57005a40b579771756ae107946c75d3f.png

Virtual components. And your set index numbers to these lines

updateTextComponent(202, "CODE " + response.code + " ERROR " + error_code);


updateTextComponent(201, "https://www.accuweather.com/images/weathericons/" + weatherData[0].WeatherIcon + ".svg");
updateTextComponent(202, weatherData[0].Temperature.Metric.Value.toFixed(0) + "°C   " + weatherData[0].WeatherText);

 

Edited by ViX
Minor errors
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...