Skip to content

Instantly share code, notes, and snippets.

@Surendrajat
Last active February 7, 2023 16:06
Embed
What would you like to do?
A Clean & Customizable Weather module for Waybar
"custom/weather": {
"exec": "python ~/.config/waybar/scripts/weather.py",
"restart-interval": 300,
"return-type": "json",
"on-click": "xdg-open https://weather.com/en-IN/weather/today/l/$(location_id)"
// "format-alt": "{alt}",
},
#custom-weather.severe {
color: #eb937d;
}
#custom-weather.sunnyDay {
color: #c2ca76;
}
#custom-weather.clearNight {
color: #2b2b2a;
}
#custom-weather.cloudyFoggyDay, #custom-weather.cloudyFoggyNight {
color: #c2ddda;
}
#custom-weather.rainyDay, #custom-weather.rainyNight {
color: #5aaca5;
}
#custom-weather.showyIcyDay, #custom-weather.snowyIcyNight {
color: #d6e7e5;
}
#custom-weather.default {
color: #dbd9d8;
}
#!/usr/bin/env python
import subprocess
from pyquery import PyQuery # install using `pip install pyquery`
import json
# weather icons
weather_icons = {
"sunnyDay": "滛",
"clearNight": "望",
"cloudyFoggyDay": "",
"cloudyFoggyNight": "",
"rainyDay": "",
"rainyNight": "",
"snowyIcyDay": "",
"snowyIcyNight": "",
"severe": "",
"default": "",
}
# get location_id
# to get your own location_id, go to https://weather.com & search your location.
# once you choose your location, you can see the location_id in the URL(64 chars long hex string)
# like this: https://weather.com/en-IN/weather/today/l/c3e96d6cc4965fc54f88296b54449571c4107c73b9638c16aafc83575b4ddf2e
location_id = "c3e96d6cc4965fc54f88296b54449571c4107c73b9638c16aafc83575b4ddf2e" # TODO
# location_id = "8139363e05edb302e2d8be35101e400084eadcecdfce5507e77d832ac0fa57ae"
# priv_env_cmd = 'cat $PRIV_ENV_FILE | grep weather_location | cut -d "=" -f 2'
# location_id = subprocess.run(
# priv_env_cmd, shell=True, capture_output=True).stdout.decode('utf8').strip()
# get html page
url = "https://weather.com/en-IN/weather/today/l/" + location_id
html_data = PyQuery(url=url)
# current temperature
temp = html_data("span[data-testid='TemperatureValue']").eq(0).text()
# print(temp)
# current status phrase
status = html_data("div[data-testid='wxPhrase']").text()
status = f"{status[:16]}.." if len(status) > 17 else status
# print(status)
# status code
status_code = html_data("#regionHeader").attr("class").split(" ")[2].split("-")[2]
# print(status_code)
# status icon
icon = (
weather_icons[status_code]
if status_code in weather_icons
else weather_icons["default"]
)
# print(icon)
# temperature feels like
temp_feel = html_data(
"div[data-testid='FeelsLikeSection'] > span[data-testid='TemperatureValue']"
).text()
temp_feel_text = f"Feels like {temp_feel}c"
# print(temp_feel_text)
# min-max temperature
temp_min = (
html_data("div[data-testid='wxData'] > span[data-testid='TemperatureValue']")
.eq(0)
.text()
)
temp_max = (
html_data("div[data-testid='wxData'] > span[data-testid='TemperatureValue']")
.eq(1)
.text()
)
temp_min_max = f" {temp_min}\t\t{temp_max}"
# print(temp_min_max)
# wind speed
wind_speed = html_data("span[data-testid='Wind']").text().split("\n")[1]
wind_text = f"煮 {wind_speed}"
# print(wind_text)
# humidity
humidity = html_data("span[data-testid='PercentageValue']").text()
humidity_text = f" {humidity}"
# print(humidity_text)
# visibility
visbility = html_data("span[data-testid='VisibilityValue']").text()
visbility_text = f" {visbility}"
# print(visbility_text)
# air quality index
air_quality_index = html_data("text[data-testid='DonutChartValue']").text()
# print(air_quality_index)
# hourly rain prediction
prediction = html_data("section[aria-label='Hourly Forecast']")(
"div[data-testid='SegmentPrecipPercentage'] > span"
).text()
prediction = prediction.replace("Chance of Rain", "")
prediction = f"\n\n  (hourly) {prediction}" if len(prediction) > 0 else prediction
# print(prediction)
# tooltip text
tooltip_text = str.format(
"\t\t{}\t\t\n{}\n{}\n{}\n\n{}\n{}\n{}{}",
f'<span size="xx-large">{temp}</span>',
f"<big>{icon}</big>",
f"<big>{status}</big>",
f"<small>{temp_feel_text}</small>",
f"<big>{temp_min_max}</big>",
f"{wind_text}\t{humidity_text}",
f"{visbility_text}\tAQI {air_quality_index}",
f"<i>{prediction}</i>",
)
# print waybar module data
out_data = {
"text": f"{icon} {temp}",
"alt": status,
"tooltip": tooltip_text,
"class": status_code,
}
print(json.dumps(out_data))
@Surendrajat
Copy link
Author

image

@StayPirate
Copy link

Could you pointed out where localtion_id in this line is defined? In my env it is not expanded and the resulting URL is https://weather.com/en-IN/weather/today/l/.

@Surendrajat
Copy link
Author

Surendrajat commented Jun 17, 2022

Could you pointed out where localtion_id in this line is defined? In my env it is not expanded and the resulting URL is https://weather.com/en-IN/weather/today/l/.

https://gist.github.com/Surendrajat/ff3876fd2166dd86fb71180f4e9342d7#file-weather-py-L21=

@StayPirate
Copy link

I already set my location from there (in weather.py), but the hash value is not available in ~/.config/waybar/config. Does it work for you?

@Surendrajat
Copy link
Author

Surendrajat commented Jun 17, 2022

Yeah that won't work unless you replace it with actual location_id or export a shell variable location_id as I do.

@StayPirate
Copy link

StayPirate commented Oct 28, 2022

I noticed today that something may have changed in the https://weather.com/en-IN/weather/today/l/... webpage and the html_data filters may now be broken.

I initially realized it because of the following error:

[2022-10-28 10:40:55.065] [error] weather stopped unexpectedly, is it endless?
Traceback (most recent call last):
  File "/home/crazybyte/.config/waybar/modules/weather", line 92, in <module>
    wind_speed = html_data(
IndexError: list index out of range

The CSS item is now class .Wind--windWrapper--3Ly7c instead of .Wind--windWrapper--3aqXJ, but fixing it is not enough to make the script works properly again.

@StayPirate
Copy link

May I ask you why are you scraping data from the web-page instead of querying some API endpoints?

@Surendrajat
Copy link
Author

Surendrajat commented Oct 29, 2022

@StayPirate Thanks for letting me know. I'm not using sway right now, but I've tried to update the script for now and hopefully made it less fragile. (Probably you can fork and maintain it if you're interested)

May I ask you why are you scraping data from the web-page instead of querying some API endpoints?

Yeah that'd be simpler, but I actively try to avoid signing up for services that are otherwise accessible.

@ShabirK21
Copy link

I get his error when i try to run, i've changed it my own location

Traceback (most recent call last):
File "/home/shabirk/.config/waybar/modules/weather.py", line 46, in
status_code = html_data("#regionHeader").attr("class").split(" ")[2].split("-")[2]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'split'

@k1tyoo
Copy link

k1tyoo commented Feb 2, 2023

Traceback (most recent call last):
File "/home/shabirk/.config/waybar/modules/weather.py", line 46, in
status_code = html_data("#regionHeader").attr("class").split(" ")[2].split("-")[2]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'split'

The new document of pyquery module shows the method below will no longer fetch the contents of the URL.

url = "https://weather.com/en-IN/weather/today/l/" + location_id
html_data = PyQuery(url)

The right way

url_fetch = "https://weather.com/en-IN/weather/today/l/" + location_id
html_data = PyQuery(url=url_fetch)

https://pyquery.readthedocs.io/en/latest/scrap.html

@Surendrajat
Copy link
Author

Surendrajat commented Feb 2, 2023

Thanks @k1tyoo for tracking it down. I've updated the script to work with pyquery 1 & 2 now.

@CerealKiller-John
Copy link

What are the default icons, as a lot of them are not showing for me, other than that I appreciate the work you have done and it's a great module :)

@Surendrajat
Copy link
Author

@CerealKiller-John thanks. I've mostly used font-awesome icons and few icons from nerd-fonts. This is my setup:

https://github.com/Surendrajat/dotfiles/blob/f3b2deef20fac21285dcefe52ed8eccdcb13fa73/.config/waybar/style.css#L48

@CerealKiller-John
Copy link

@CerealKiller-John thanks. I've mostly used font-awesome icons and few icons from nerd-fonts. This is my setup:

https://github.com/Surendrajat/dotfiles/blob/f3b2deef20fac21285dcefe52ed8eccdcb13fa73/.config/waybar/style.css#L48

Thank you, I also was wondering is there a way to incorporate current location into this? As I am using it on a laptop, I wouldn't really need to know my home forecast if I am not at home. I know I could update the key, but I was wondering if there was an easier way?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment