diff --git a/weather-widget/README.md b/weather-widget/README.md
index 730c1f5..defe1b7 100644
--- a/weather-widget/README.md
+++ b/weather-widget/README.md
@@ -1,8 +1,13 @@
# Weather widget
-![Weather Widget](./weather-widget.png)
+
+
+
-Note that widget uses the Arc icon theme, so it should be [installed](https://github.com/horst3180/arc-icon-theme#installation) first under **/usr/share/icons/Arc/** folder.
+Widget consists of three sections:
+ - current weather, including humidity, wind speed, UV index
+ - hourly forecast for the next 24 hours
+ - daily forecast for the next five days
## Customization
@@ -10,34 +15,63 @@ It is possible to customize widget by providing a table with all or some of the
| Name | Default | Description |
|---|---|---|
-| `font` | `Play 9` | Font |
-| `city` | `Montreal,ca` | City name and country code, [more info](https://openweathermap.org/current) |
-| `api_key` | none| API key, required |
-| `units` | `metric` | `metric` for celsius, `imperial` for fahrenheit |
-| `both_units_widget` | `false` | show temperature in both units (15°C (59°F)) or in one (15°C) |
-| `both_units_popup` | `false` | same as above but for popup |
-| `notification_position` | `top_right` | The notification position |
+| coordinates | Required | Table with two elements: latitude and longitude, e.g. `{46.204400, 6.143200}` |
+| api_key | Required | Get it [here](https://openweathermap.org/appid) |
+| font_name | `beautiful.font:gsub("%s%d+$", "")` | **Name** of the font to use e.g. 'Play' |
+| both_units_widget | false | Show temperature in both units - '28°C (83°F) |
+| units | metric | `metric` for celsius, `imperial` for fahrenheit |
+| show_hourly_forecast | false | Show hourly forecase section |
+| time_format_12h |false | 12 or 24 hour format (13:00 - default or 1pm) |
+| show_daily_forecast | false | Show daily forecast section |
+| icon_pack_name | weather-underground-icons | Name of the icon pack, could be `weather-underground-icon` or `VitalyGorbachev` or create your own, more details below |
+| icons_extension | `.svg` | File extension of icons in the pack |
-### Example:
+### Icons:
+
+Widget comes with two predefined icon packs:
+
+ - weather-underground-icons taken from [here](https://github.com/manifestinteractive/weather-underground-icons)
+ - VitalyGorbachev taken from [here](https://www.flaticon.com/authors/vitaly-gorbachev)
+
+To add your custom icons, create a folder with the pack name under `/icons` and use the folder name in widget's config. There should be 18 icons, preferably 128x128 minimum. Icons should also respect the naming convention, please check widget's source.
+
+### Examples:
+
+
+#### Custom font, icons
+
+![example1](./screenshots/example1.png)
```lua
-weather_widget({
- api_key = 'your-api-key',
+weather_curl_widget({
+ api_key='',
+ coordinates = {45.5017, -73.5673},
+ time_format_12h = true,
units = 'imperial',
- font = 'Ubuntu Mono 9'
+ both_units_widget = true,
+ font_name = 'Carter One',
+ icons = 'VitalyGorbachev',
+ show_hourly_forecast = true,
+ show_daily_forecast = true,
+}),
+```
+
+#### Only current weather
+
+![example2](./screenshots/example2.png)
+
+```lua
+weather_curl_widget({
+ api_key='',
+ coordinates = {45.5017, -73.5673},
}),
```
+
## Installation
-1. Install lua socket - to make HTTP calls to get the weather information.
-
- ```bash
- $ sudo apt-get install lua-socket
- ```
-
1. Download json parser for lua from [github.com/rxi/json.lua](https://github.com/rxi/json.lua) and place it under **~/.config/awesome/** (don't forget to star a repo ):
```bash
@@ -66,15 +100,27 @@ weather_widget({
layout = wibox.layout.fixed.horizontal,
...
--default
- weather_widget({api_key = 'your-api-key'}),
- --customized
weather_widget({
- api_key = 'your-api-key',
+ coordinates = {45.5017, -73.5673},
+ api_key='c3d7320b359da4e48c2d682a04076576',
+ }),
+ ,
+ --customized
+ weather_curl_widget({
+ api_key='',
+ coordinates = {45.5017, -73.5673},
+ time_format_12h = true,
units = 'imperial',
- font = 'Ubuntu Mono 9'
- })
+ both_units_widget = true,
+ font_name = 'Carter One',
+ icons = 'VitalyGorbachev',
+ show_hourly_forecast = true,
+ show_daily_forecast = true,
+ }),
...
```
-You can read how it works in more details [here](http://pavelmakhov.com/2017/02/weather-widget-for-awesome-wm)
+## How it works
+
+TBW
\ No newline at end of file
diff --git a/weather-widget/icons/VitalyGorbachev/broken-clouds-night.svg b/weather-widget/icons/VitalyGorbachev/broken-clouds-night.svg
new file mode 100644
index 0000000..8b7fc48
--- /dev/null
+++ b/weather-widget/icons/VitalyGorbachev/broken-clouds-night.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/weather-widget/icons/VitalyGorbachev/broken-clouds.svg b/weather-widget/icons/VitalyGorbachev/broken-clouds.svg
new file mode 100644
index 0000000..d42ea59
--- /dev/null
+++ b/weather-widget/icons/VitalyGorbachev/broken-clouds.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/weather-widget/icons/VitalyGorbachev/clear-sky-night.svg b/weather-widget/icons/VitalyGorbachev/clear-sky-night.svg
new file mode 100644
index 0000000..44f096c
--- /dev/null
+++ b/weather-widget/icons/VitalyGorbachev/clear-sky-night.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/weather-widget/icons/VitalyGorbachev/clear-sky.svg b/weather-widget/icons/VitalyGorbachev/clear-sky.svg
new file mode 100644
index 0000000..dc82163
--- /dev/null
+++ b/weather-widget/icons/VitalyGorbachev/clear-sky.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/weather-widget/icons/VitalyGorbachev/few-clouds-night.svg b/weather-widget/icons/VitalyGorbachev/few-clouds-night.svg
new file mode 100644
index 0000000..8b7fc48
--- /dev/null
+++ b/weather-widget/icons/VitalyGorbachev/few-clouds-night.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/weather-widget/icons/VitalyGorbachev/few-clouds.svg b/weather-widget/icons/VitalyGorbachev/few-clouds.svg
new file mode 100644
index 0000000..d42ea59
--- /dev/null
+++ b/weather-widget/icons/VitalyGorbachev/few-clouds.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/weather-widget/icons/VitalyGorbachev/mist-night.svg b/weather-widget/icons/VitalyGorbachev/mist-night.svg
new file mode 100644
index 0000000..960b07d
--- /dev/null
+++ b/weather-widget/icons/VitalyGorbachev/mist-night.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/weather-widget/icons/VitalyGorbachev/mist.svg b/weather-widget/icons/VitalyGorbachev/mist.svg
new file mode 100644
index 0000000..770f8d7
--- /dev/null
+++ b/weather-widget/icons/VitalyGorbachev/mist.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/weather-widget/icons/VitalyGorbachev/rain-night.svg b/weather-widget/icons/VitalyGorbachev/rain-night.svg
new file mode 100644
index 0000000..11ecf00
--- /dev/null
+++ b/weather-widget/icons/VitalyGorbachev/rain-night.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/weather-widget/icons/VitalyGorbachev/rain.svg b/weather-widget/icons/VitalyGorbachev/rain.svg
new file mode 100644
index 0000000..11ecf00
--- /dev/null
+++ b/weather-widget/icons/VitalyGorbachev/rain.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/weather-widget/icons/VitalyGorbachev/scattered-clouds-night.svg b/weather-widget/icons/VitalyGorbachev/scattered-clouds-night.svg
new file mode 100644
index 0000000..8b7fc48
--- /dev/null
+++ b/weather-widget/icons/VitalyGorbachev/scattered-clouds-night.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/weather-widget/icons/VitalyGorbachev/scattered-clouds.svg b/weather-widget/icons/VitalyGorbachev/scattered-clouds.svg
new file mode 100644
index 0000000..d42ea59
--- /dev/null
+++ b/weather-widget/icons/VitalyGorbachev/scattered-clouds.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/weather-widget/icons/VitalyGorbachev/shower-rain-night.svg b/weather-widget/icons/VitalyGorbachev/shower-rain-night.svg
new file mode 100644
index 0000000..4d1897c
--- /dev/null
+++ b/weather-widget/icons/VitalyGorbachev/shower-rain-night.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/weather-widget/icons/VitalyGorbachev/shower-rain.svg b/weather-widget/icons/VitalyGorbachev/shower-rain.svg
new file mode 100644
index 0000000..4d1897c
--- /dev/null
+++ b/weather-widget/icons/VitalyGorbachev/shower-rain.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/weather-widget/icons/VitalyGorbachev/snow-night.svg b/weather-widget/icons/VitalyGorbachev/snow-night.svg
new file mode 100644
index 0000000..bee891e
--- /dev/null
+++ b/weather-widget/icons/VitalyGorbachev/snow-night.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/weather-widget/icons/VitalyGorbachev/snow.svg b/weather-widget/icons/VitalyGorbachev/snow.svg
new file mode 100644
index 0000000..e2ea140
--- /dev/null
+++ b/weather-widget/icons/VitalyGorbachev/snow.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/weather-widget/icons/VitalyGorbachev/thunderstorm-night.svg b/weather-widget/icons/VitalyGorbachev/thunderstorm-night.svg
new file mode 100644
index 0000000..1813197
--- /dev/null
+++ b/weather-widget/icons/VitalyGorbachev/thunderstorm-night.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/weather-widget/icons/VitalyGorbachev/thunderstorm.svg b/weather-widget/icons/VitalyGorbachev/thunderstorm.svg
new file mode 100644
index 0000000..44a733c
--- /dev/null
+++ b/weather-widget/icons/VitalyGorbachev/thunderstorm.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/weather-widget/icons/weather-underground-icons/broken-clouds-night.png b/weather-widget/icons/weather-underground-icons/broken-clouds-night.png
new file mode 100644
index 0000000..061d1cd
Binary files /dev/null and b/weather-widget/icons/weather-underground-icons/broken-clouds-night.png differ
diff --git a/weather-widget/icons/weather-underground-icons/broken-clouds.png b/weather-widget/icons/weather-underground-icons/broken-clouds.png
new file mode 100755
index 0000000..5967d92
Binary files /dev/null and b/weather-widget/icons/weather-underground-icons/broken-clouds.png differ
diff --git a/weather-widget/icons/weather-underground-icons/clear-sky-night.png b/weather-widget/icons/weather-underground-icons/clear-sky-night.png
new file mode 100644
index 0000000..cc40d0f
Binary files /dev/null and b/weather-widget/icons/weather-underground-icons/clear-sky-night.png differ
diff --git a/weather-widget/icons/weather-underground-icons/clear-sky.png b/weather-widget/icons/weather-underground-icons/clear-sky.png
new file mode 100755
index 0000000..acf8e5c
Binary files /dev/null and b/weather-widget/icons/weather-underground-icons/clear-sky.png differ
diff --git a/weather-widget/icons/weather-underground-icons/few-clouds-night.png b/weather-widget/icons/weather-underground-icons/few-clouds-night.png
new file mode 100644
index 0000000..9c34fab
Binary files /dev/null and b/weather-widget/icons/weather-underground-icons/few-clouds-night.png differ
diff --git a/weather-widget/icons/weather-underground-icons/few-clouds.png b/weather-widget/icons/weather-underground-icons/few-clouds.png
new file mode 100755
index 0000000..7580fc5
Binary files /dev/null and b/weather-widget/icons/weather-underground-icons/few-clouds.png differ
diff --git a/weather-widget/icons/weather-underground-icons/mist-night.png b/weather-widget/icons/weather-underground-icons/mist-night.png
new file mode 100755
index 0000000..102142a
Binary files /dev/null and b/weather-widget/icons/weather-underground-icons/mist-night.png differ
diff --git a/weather-widget/icons/weather-underground-icons/mist.png b/weather-widget/icons/weather-underground-icons/mist.png
new file mode 100755
index 0000000..102142a
Binary files /dev/null and b/weather-widget/icons/weather-underground-icons/mist.png differ
diff --git a/weather-widget/icons/weather-underground-icons/rain-night.png b/weather-widget/icons/weather-underground-icons/rain-night.png
new file mode 100755
index 0000000..49f0903
Binary files /dev/null and b/weather-widget/icons/weather-underground-icons/rain-night.png differ
diff --git a/weather-widget/icons/weather-underground-icons/rain.png b/weather-widget/icons/weather-underground-icons/rain.png
new file mode 100755
index 0000000..49f0903
Binary files /dev/null and b/weather-widget/icons/weather-underground-icons/rain.png differ
diff --git a/weather-widget/icons/weather-underground-icons/scattered-clouds-night.png b/weather-widget/icons/weather-underground-icons/scattered-clouds-night.png
new file mode 100755
index 0000000..63cb1b2
Binary files /dev/null and b/weather-widget/icons/weather-underground-icons/scattered-clouds-night.png differ
diff --git a/weather-widget/icons/weather-underground-icons/scattered-clouds.png b/weather-widget/icons/weather-underground-icons/scattered-clouds.png
new file mode 100755
index 0000000..63cb1b2
Binary files /dev/null and b/weather-widget/icons/weather-underground-icons/scattered-clouds.png differ
diff --git a/weather-widget/icons/weather-underground-icons/shower-rain-night.png b/weather-widget/icons/weather-underground-icons/shower-rain-night.png
new file mode 100755
index 0000000..49f0903
Binary files /dev/null and b/weather-widget/icons/weather-underground-icons/shower-rain-night.png differ
diff --git a/weather-widget/icons/weather-underground-icons/shower-rain.png b/weather-widget/icons/weather-underground-icons/shower-rain.png
new file mode 100755
index 0000000..49f0903
Binary files /dev/null and b/weather-widget/icons/weather-underground-icons/shower-rain.png differ
diff --git a/weather-widget/icons/weather-underground-icons/snow-night.png b/weather-widget/icons/weather-underground-icons/snow-night.png
new file mode 100755
index 0000000..0a7f006
Binary files /dev/null and b/weather-widget/icons/weather-underground-icons/snow-night.png differ
diff --git a/weather-widget/icons/weather-underground-icons/snow.png b/weather-widget/icons/weather-underground-icons/snow.png
new file mode 100755
index 0000000..0a7f006
Binary files /dev/null and b/weather-widget/icons/weather-underground-icons/snow.png differ
diff --git a/weather-widget/icons/weather-underground-icons/thunderstorm-night.png b/weather-widget/icons/weather-underground-icons/thunderstorm-night.png
new file mode 100755
index 0000000..2102104
Binary files /dev/null and b/weather-widget/icons/weather-underground-icons/thunderstorm-night.png differ
diff --git a/weather-widget/icons/weather-underground-icons/thunderstorm.png b/weather-widget/icons/weather-underground-icons/thunderstorm.png
new file mode 100755
index 0000000..2102104
Binary files /dev/null and b/weather-widget/icons/weather-underground-icons/thunderstorm.png differ
diff --git a/weather-widget/screenshots/example1.png b/weather-widget/screenshots/example1.png
new file mode 100644
index 0000000..7074faa
Binary files /dev/null and b/weather-widget/screenshots/example1.png differ
diff --git a/weather-widget/screenshots/example2.png b/weather-widget/screenshots/example2.png
new file mode 100644
index 0000000..857274b
Binary files /dev/null and b/weather-widget/screenshots/example2.png differ
diff --git a/weather-widget/screenshots/weather-widget.png b/weather-widget/screenshots/weather-widget.png
new file mode 100644
index 0000000..c7fc37e
Binary files /dev/null and b/weather-widget/screenshots/weather-widget.png differ
diff --git a/weather-widget/weather.json b/weather-widget/weather.json
new file mode 100644
index 0000000..4a18714
--- /dev/null
+++ b/weather-widget/weather.json
@@ -0,0 +1,1224 @@
+{
+ "lat": 33.44,
+ "lon": -94.04,
+ "timezone": "America/Chicago",
+ "timezone_offset": -18000,
+ "current": {
+ "dt": 1594490161,
+ "sunrise": 1594466108,
+ "sunset": 1594517275,
+ "temp": 32.69,
+ "feels_like": 36.81,
+ "pressure": 1017,
+ "humidity": 59,
+ "dew_point": 23.63,
+ "uvi": 7.94,
+ "clouds": 40,
+ "visibility": 16093,
+ "wind_speed": 2.1,
+ "wind_deg": 250,
+ "weather": [
+ {
+ "id": 802,
+ "main": "Clear Sky",
+ "description": "clear sky",
+ "icon": "01d"
+ }
+ ]
+ },
+ "hourly": [
+ {
+ "dt": 1594486800,
+ "temp": 32.69,
+ "feels_like": 36.4,
+ "pressure": 1017,
+ "humidity": 59,
+ "dew_point": 23.63,
+ "clouds": 40,
+ "wind_speed": 2.68,
+ "wind_deg": 242,
+ "weather": [
+ {
+ "id": 802,
+ "main": "Clouds",
+ "description": "scattered clouds",
+ "icon": "03d"
+ }
+ ]
+ },
+ {
+ "dt": 1594490400,
+ "temp": 33.38,
+ "feels_like": 37.27,
+ "pressure": 1017,
+ "humidity": 59,
+ "dew_point": 24.27,
+ "clouds": 21,
+ "wind_speed": 2.97,
+ "wind_deg": 248,
+ "weather": [
+ {
+ "id": 801,
+ "main": "Clouds",
+ "description": "few clouds",
+ "icon": "02d"
+ }
+ ]
+ },
+ {
+ "dt": 1594494000,
+ "temp": 34.44,
+ "feels_like": 38.38,
+ "pressure": 1015,
+ "humidity": 55,
+ "dew_point": 24.09,
+ "clouds": 28,
+ "wind_speed": 2.74,
+ "wind_deg": 254,
+ "weather": [
+ {
+ "id": 802,
+ "main": "Clouds",
+ "description": "scattered clouds",
+ "icon": "03d"
+ }
+ ]
+ },
+ {
+ "dt": 1594497600,
+ "temp": 35.32,
+ "feels_like": 39.58,
+ "pressure": 1014,
+ "humidity": 53,
+ "dew_point": 24.28,
+ "clouds": 18,
+ "wind_speed": 2.45,
+ "wind_deg": 260,
+ "weather": [
+ {
+ "id": 801,
+ "main": "Clouds",
+ "description": "few clouds",
+ "icon": "02d"
+ }
+ ]
+ },
+ {
+ "dt": 1594501200,
+ "temp": 35.57,
+ "feels_like": 39.73,
+ "pressure": 1013,
+ "humidity": 52,
+ "dew_point": 24.19,
+ "clouds": 16,
+ "wind_speed": 2.52,
+ "wind_deg": 259,
+ "weather": [
+ {
+ "id": 801,
+ "main": "Clouds",
+ "description": "few clouds",
+ "icon": "02d"
+ }
+ ]
+ },
+ {
+ "dt": 1594504800,
+ "temp": 35.34,
+ "feels_like": 39.82,
+ "pressure": 1012,
+ "humidity": 55,
+ "dew_point": 25.07,
+ "clouds": 13,
+ "wind_speed": 2.68,
+ "wind_deg": 256,
+ "weather": [
+ {
+ "id": 801,
+ "main": "Clouds",
+ "description": "few clouds",
+ "icon": "02d"
+ }
+ ]
+ },
+ {
+ "dt": 1594508400,
+ "temp": 34.85,
+ "feels_like": 40.34,
+ "pressure": 1012,
+ "humidity": 61,
+ "dew_point": 26.36,
+ "clouds": 10,
+ "wind_speed": 2.42,
+ "wind_deg": 249,
+ "weather": [
+ {
+ "id": 800,
+ "main": "Clear",
+ "description": "clear sky",
+ "icon": "01d"
+ }
+ ]
+ },
+ {
+ "dt": 1594512000,
+ "temp": 33.39,
+ "feels_like": 39.08,
+ "pressure": 1012,
+ "humidity": 67,
+ "dew_point": 26.53,
+ "clouds": 9,
+ "wind_speed": 2.34,
+ "wind_deg": 246,
+ "weather": [
+ {
+ "id": 800,
+ "main": "Clear",
+ "description": "clear sky",
+ "icon": "01d"
+ }
+ ]
+ },
+ {
+ "dt": 1594515600,
+ "temp": 30.82,
+ "feels_like": 35.85,
+ "pressure": 1012,
+ "humidity": 73,
+ "dew_point": 25.61,
+ "clouds": 0,
+ "wind_speed": 2.35,
+ "wind_deg": 240,
+ "weather": [
+ {
+ "id": 800,
+ "main": "Clear",
+ "description": "clear sky",
+ "icon": "01d"
+ }
+ ]
+ },
+ {
+ "dt": 1594519200,
+ "temp": 29.56,
+ "feels_like": 33.64,
+ "pressure": 1012,
+ "humidity": 73,
+ "dew_point": 24.34,
+ "clouds": 0,
+ "wind_speed": 2.65,
+ "wind_deg": 227,
+ "weather": [
+ {
+ "id": 800,
+ "main": "Clear",
+ "description": "clear sky",
+ "icon": "01n"
+ }
+ ]
+ },
+ {
+ "dt": 1594522800,
+ "temp": 28.72,
+ "feels_like": 32.55,
+ "pressure": 1012,
+ "humidity": 75,
+ "dew_point": 23.97,
+ "clouds": 0,
+ "wind_speed": 2.7,
+ "wind_deg": 223,
+ "weather": [
+ {
+ "id": 800,
+ "main": "Clear",
+ "description": "clear sky",
+ "icon": "01n"
+ }
+ ]
+ },
+ {
+ "dt": 1594526400,
+ "temp": 27.79,
+ "feels_like": 31.42,
+ "pressure": 1012,
+ "humidity": 77,
+ "dew_point": 23.56,
+ "clouds": 0,
+ "wind_speed": 2.61,
+ "wind_deg": 211,
+ "weather": [
+ {
+ "id": 800,
+ "main": "Clear",
+ "description": "clear sky",
+ "icon": "01n"
+ }
+ ]
+ },
+ {
+ "dt": 1594530000,
+ "temp": 27.27,
+ "feels_like": 30.91,
+ "pressure": 1012,
+ "humidity": 79,
+ "dew_point": 23.39,
+ "clouds": 0,
+ "wind_speed": 2.54,
+ "wind_deg": 226,
+ "weather": [
+ {
+ "id": 800,
+ "main": "Clear",
+ "description": "clear sky",
+ "icon": "01n"
+ }
+ ]
+ },
+ {
+ "dt": 1594533600,
+ "temp": 27.17,
+ "feels_like": 30.72,
+ "pressure": 1012,
+ "humidity": 79,
+ "dew_point": 23.23,
+ "clouds": 0,
+ "wind_speed": 2.58,
+ "wind_deg": 213,
+ "weather": [
+ {
+ "id": 800,
+ "main": "Clear",
+ "description": "clear sky",
+ "icon": "01n"
+ }
+ ]
+ },
+ {
+ "dt": 1594537200,
+ "temp": 26.91,
+ "feels_like": 30.06,
+ "pressure": 1012,
+ "humidity": 78,
+ "dew_point": 22.95,
+ "clouds": 0,
+ "wind_speed": 2.79,
+ "wind_deg": 225,
+ "weather": [
+ {
+ "id": 800,
+ "main": "Clear",
+ "description": "clear sky",
+ "icon": "01n"
+ }
+ ]
+ },
+ {
+ "dt": 1594540800,
+ "temp": 26.57,
+ "feels_like": 29.76,
+ "pressure": 1011,
+ "humidity": 79,
+ "dew_point": 22.68,
+ "clouds": 33,
+ "wind_speed": 2.63,
+ "wind_deg": 217,
+ "weather": [
+ {
+ "id": 802,
+ "main": "Clouds",
+ "description": "scattered clouds",
+ "icon": "03n"
+ }
+ ]
+ },
+ {
+ "dt": 1594544400,
+ "temp": 26.39,
+ "feels_like": 28.98,
+ "pressure": 1011,
+ "humidity": 78,
+ "dew_point": 22.36,
+ "clouds": 56,
+ "wind_speed": 3.19,
+ "wind_deg": 191,
+ "weather": [
+ {
+ "id": 803,
+ "main": "Clouds",
+ "description": "broken clouds",
+ "icon": "04n"
+ }
+ ]
+ },
+ {
+ "dt": 1594548000,
+ "temp": 25.97,
+ "feels_like": 29.15,
+ "pressure": 1011,
+ "humidity": 79,
+ "dew_point": 22.12,
+ "clouds": 67,
+ "wind_speed": 2.2,
+ "wind_deg": 211,
+ "weather": [
+ {
+ "id": 803,
+ "main": "Clouds",
+ "description": "broken clouds",
+ "icon": "04n"
+ }
+ ]
+ },
+ {
+ "dt": 1594551600,
+ "temp": 25.6,
+ "feels_like": 28.65,
+ "pressure": 1012,
+ "humidity": 79,
+ "dew_point": 21.77,
+ "clouds": 72,
+ "wind_speed": 2.12,
+ "wind_deg": 257,
+ "weather": [
+ {
+ "id": 803,
+ "main": "Clouds",
+ "description": "broken clouds",
+ "icon": "04n"
+ }
+ ]
+ },
+ {
+ "dt": 1594555200,
+ "temp": 25.25,
+ "feels_like": 29.31,
+ "pressure": 1012,
+ "humidity": 84,
+ "dew_point": 22.42,
+ "clouds": 74,
+ "wind_speed": 1.18,
+ "wind_deg": 276,
+ "weather": [
+ {
+ "id": 500,
+ "main": "Rain",
+ "description": "light rain",
+ "icon": "10d"
+ }
+ ],
+ "rain": {
+ "1h": 0.66
+ }
+ },
+ {
+ "dt": 1594558800,
+ "temp": 26.51,
+ "feels_like": 31.02,
+ "pressure": 1013,
+ "humidity": 82,
+ "dew_point": 23.22,
+ "clouds": 91,
+ "wind_speed": 1.19,
+ "wind_deg": 227,
+ "weather": [
+ {
+ "id": 500,
+ "main": "Rain",
+ "description": "light rain",
+ "icon": "10d"
+ }
+ ],
+ "rain": {
+ "1h": 0.5
+ }
+ },
+ {
+ "dt": 1594562400,
+ "temp": 27.99,
+ "feels_like": 31.34,
+ "pressure": 1012,
+ "humidity": 75,
+ "dew_point": 23.26,
+ "clouds": 87,
+ "wind_speed": 2.82,
+ "wind_deg": 223,
+ "weather": [
+ {
+ "id": 804,
+ "main": "Clouds",
+ "description": "overcast clouds",
+ "icon": "04d"
+ }
+ ]
+ },
+ {
+ "dt": 1594566000,
+ "temp": 29.6,
+ "feels_like": 32.28,
+ "pressure": 1012,
+ "humidity": 67,
+ "dew_point": 23.02,
+ "clouds": 63,
+ "wind_speed": 3.51,
+ "wind_deg": 236,
+ "weather": [
+ {
+ "id": 803,
+ "main": "Clouds",
+ "description": "broken clouds",
+ "icon": "04d"
+ }
+ ]
+ },
+ {
+ "dt": 1594569600,
+ "temp": 31.12,
+ "feels_like": 33.49,
+ "pressure": 1011,
+ "humidity": 62,
+ "dew_point": 23.22,
+ "clouds": 52,
+ "wind_speed": 4.08,
+ "wind_deg": 239,
+ "weather": [
+ {
+ "id": 803,
+ "main": "Clouds",
+ "description": "broken clouds",
+ "icon": "04d"
+ }
+ ]
+ },
+ {
+ "dt": 1594573200,
+ "temp": 32.66,
+ "feels_like": 35.05,
+ "pressure": 1011,
+ "humidity": 58,
+ "dew_point": 23.6,
+ "clouds": 46,
+ "wind_speed": 4.31,
+ "wind_deg": 238,
+ "weather": [
+ {
+ "id": 802,
+ "main": "Clouds",
+ "description": "scattered clouds",
+ "icon": "03d"
+ }
+ ]
+ },
+ {
+ "dt": 1594576800,
+ "temp": 34.15,
+ "feels_like": 36.59,
+ "pressure": 1011,
+ "humidity": 54,
+ "dew_point": 23.74,
+ "clouds": 40,
+ "wind_speed": 4.4,
+ "wind_deg": 236,
+ "weather": [
+ {
+ "id": 802,
+ "main": "Clouds",
+ "description": "scattered clouds",
+ "icon": "03d"
+ }
+ ]
+ },
+ {
+ "dt": 1594580400,
+ "temp": 35.24,
+ "feels_like": 37.8,
+ "pressure": 1010,
+ "humidity": 51,
+ "dew_point": 23.83,
+ "clouds": 0,
+ "wind_speed": 4.27,
+ "wind_deg": 246,
+ "weather": [
+ {
+ "id": 800,
+ "main": "Clear",
+ "description": "clear sky",
+ "icon": "01d"
+ }
+ ]
+ },
+ {
+ "dt": 1594584000,
+ "temp": 35.73,
+ "feels_like": 38.39,
+ "pressure": 1009,
+ "humidity": 50,
+ "dew_point": 23.92,
+ "clouds": 0,
+ "wind_speed": 4.23,
+ "wind_deg": 262,
+ "weather": [
+ {
+ "id": 800,
+ "main": "Clear",
+ "description": "clear sky",
+ "icon": "01d"
+ }
+ ]
+ },
+ {
+ "dt": 1594587600,
+ "temp": 35.89,
+ "feels_like": 38.87,
+ "pressure": 1008,
+ "humidity": 51,
+ "dew_point": 24.29,
+ "clouds": 0,
+ "wind_speed": 4.17,
+ "wind_deg": 267,
+ "weather": [
+ {
+ "id": 800,
+ "main": "Clear",
+ "description": "clear sky",
+ "icon": "01d"
+ }
+ ]
+ },
+ {
+ "dt": 1594591200,
+ "temp": 35.63,
+ "feels_like": 39.46,
+ "pressure": 1008,
+ "humidity": 55,
+ "dew_point": 25.31,
+ "clouds": 0,
+ "wind_speed": 3.85,
+ "wind_deg": 266,
+ "weather": [
+ {
+ "id": 800,
+ "main": "Clear",
+ "description": "clear sky",
+ "icon": "01d"
+ }
+ ]
+ },
+ {
+ "dt": 1594594800,
+ "temp": 35,
+ "feels_like": 39.97,
+ "pressure": 1007,
+ "humidity": 61,
+ "dew_point": 26.37,
+ "clouds": 0,
+ "wind_speed": 3.3,
+ "wind_deg": 262,
+ "weather": [
+ {
+ "id": 800,
+ "main": "Clear",
+ "description": "clear sky",
+ "icon": "01d"
+ }
+ ]
+ },
+ {
+ "dt": 1594598400,
+ "temp": 33.25,
+ "feels_like": 38.23,
+ "pressure": 1007,
+ "humidity": 66,
+ "dew_point": 26.08,
+ "clouds": 0,
+ "wind_speed": 2.98,
+ "wind_deg": 259,
+ "weather": [
+ {
+ "id": 800,
+ "main": "Clear",
+ "description": "clear sky",
+ "icon": "01d"
+ }
+ ]
+ },
+ {
+ "dt": 1594602000,
+ "temp": 30.86,
+ "feels_like": 35.71,
+ "pressure": 1008,
+ "humidity": 72,
+ "dew_point": 25.37,
+ "clouds": 0,
+ "wind_speed": 2.44,
+ "wind_deg": 254,
+ "weather": [
+ {
+ "id": 800,
+ "main": "Clear",
+ "description": "clear sky",
+ "icon": "01d"
+ }
+ ]
+ },
+ {
+ "dt": 1594605600,
+ "temp": 29.71,
+ "feels_like": 34.14,
+ "pressure": 1008,
+ "humidity": 72,
+ "dew_point": 24.24,
+ "clouds": 0,
+ "wind_speed": 2.07,
+ "wind_deg": 246,
+ "weather": [
+ {
+ "id": 800,
+ "main": "Clear",
+ "description": "clear sky",
+ "icon": "01n"
+ }
+ ]
+ },
+ {
+ "dt": 1594609200,
+ "temp": 29.09,
+ "feels_like": 33.6,
+ "pressure": 1009,
+ "humidity": 73,
+ "dew_point": 23.92,
+ "clouds": 0,
+ "wind_speed": 1.65,
+ "wind_deg": 239,
+ "weather": [
+ {
+ "id": 800,
+ "main": "Clear",
+ "description": "clear sky",
+ "icon": "01n"
+ }
+ ]
+ },
+ {
+ "dt": 1594612800,
+ "temp": 28.41,
+ "feels_like": 33.33,
+ "pressure": 1009,
+ "humidity": 75,
+ "dew_point": 23.77,
+ "clouds": 0,
+ "wind_speed": 0.9,
+ "wind_deg": 301,
+ "weather": [
+ {
+ "id": 800,
+ "main": "Clear",
+ "description": "clear sky",
+ "icon": "01n"
+ }
+ ]
+ },
+ {
+ "dt": 1594616400,
+ "temp": 27.65,
+ "feels_like": 31.94,
+ "pressure": 1010,
+ "humidity": 79,
+ "dew_point": 23.7,
+ "clouds": 0,
+ "wind_speed": 1.9,
+ "wind_deg": 342,
+ "weather": [
+ {
+ "id": 800,
+ "main": "Clear",
+ "description": "clear sky",
+ "icon": "01n"
+ }
+ ]
+ },
+ {
+ "dt": 1594620000,
+ "temp": 26.81,
+ "feels_like": 30.18,
+ "pressure": 1010,
+ "humidity": 78,
+ "dew_point": 22.81,
+ "clouds": 0,
+ "wind_speed": 2.39,
+ "wind_deg": 13,
+ "weather": [
+ {
+ "id": 800,
+ "main": "Clear",
+ "description": "clear sky",
+ "icon": "01n"
+ }
+ ]
+ },
+ {
+ "dt": 1594623600,
+ "temp": 25.86,
+ "feels_like": 28.37,
+ "pressure": 1010,
+ "humidity": 75,
+ "dew_point": 21.3,
+ "clouds": 0,
+ "wind_speed": 2.45,
+ "wind_deg": 41,
+ "weather": [
+ {
+ "id": 800,
+ "main": "Clear",
+ "description": "clear sky",
+ "icon": "01n"
+ }
+ ]
+ },
+ {
+ "dt": 1594627200,
+ "temp": 24.94,
+ "feels_like": 26.96,
+ "pressure": 1010,
+ "humidity": 75,
+ "dew_point": 20.36,
+ "clouds": 0,
+ "wind_speed": 2.53,
+ "wind_deg": 49,
+ "weather": [
+ {
+ "id": 800,
+ "main": "Clear",
+ "description": "clear sky",
+ "icon": "01n"
+ }
+ ]
+ },
+ {
+ "dt": 1594630800,
+ "temp": 23.94,
+ "feels_like": 25.44,
+ "pressure": 1010,
+ "humidity": 78,
+ "dew_point": 19.9,
+ "clouds": 2,
+ "wind_speed": 3.05,
+ "wind_deg": 56,
+ "weather": [
+ {
+ "id": 800,
+ "main": "Clear",
+ "description": "clear sky",
+ "icon": "01n"
+ }
+ ]
+ },
+ {
+ "dt": 1594634400,
+ "temp": 23.22,
+ "feels_like": 25.05,
+ "pressure": 1011,
+ "humidity": 81,
+ "dew_point": 19.94,
+ "clouds": 14,
+ "wind_speed": 2.51,
+ "wind_deg": 65,
+ "weather": [
+ {
+ "id": 801,
+ "main": "Clouds",
+ "description": "few clouds",
+ "icon": "02n"
+ }
+ ]
+ },
+ {
+ "dt": 1594638000,
+ "temp": 22.87,
+ "feels_like": 24.56,
+ "pressure": 1011,
+ "humidity": 81,
+ "dew_point": 19.61,
+ "clouds": 17,
+ "wind_speed": 2.48,
+ "wind_deg": 74,
+ "weather": [
+ {
+ "id": 801,
+ "main": "Clouds",
+ "description": "few clouds",
+ "icon": "02n"
+ }
+ ]
+ },
+ {
+ "dt": 1594641600,
+ "temp": 22.82,
+ "feels_like": 24.7,
+ "pressure": 1012,
+ "humidity": 83,
+ "dew_point": 19.8,
+ "clouds": 29,
+ "wind_speed": 2.44,
+ "wind_deg": 63,
+ "weather": [
+ {
+ "id": 802,
+ "main": "Clouds",
+ "description": "scattered clouds",
+ "icon": "03d"
+ }
+ ]
+ },
+ {
+ "dt": 1594645200,
+ "temp": 23.84,
+ "feels_like": 25.88,
+ "pressure": 1013,
+ "humidity": 80,
+ "dew_point": 20.21,
+ "clouds": 100,
+ "wind_speed": 2.48,
+ "wind_deg": 62,
+ "weather": [
+ {
+ "id": 804,
+ "main": "Clouds",
+ "description": "overcast clouds",
+ "icon": "04d"
+ }
+ ]
+ },
+ {
+ "dt": 1594648800,
+ "temp": 25,
+ "feels_like": 27,
+ "pressure": 1013,
+ "humidity": 75,
+ "dew_point": 20.37,
+ "clouds": 100,
+ "wind_speed": 2.59,
+ "wind_deg": 67,
+ "weather": [
+ {
+ "id": 804,
+ "main": "Clouds",
+ "description": "overcast clouds",
+ "icon": "04d"
+ }
+ ]
+ },
+ {
+ "dt": 1594652400,
+ "temp": 26.18,
+ "feels_like": 27.69,
+ "pressure": 1012,
+ "humidity": 73,
+ "dew_point": 21.06,
+ "clouds": 100,
+ "wind_speed": 3.79,
+ "wind_deg": 101,
+ "weather": [
+ {
+ "id": 804,
+ "main": "Clouds",
+ "description": "overcast clouds",
+ "icon": "04d"
+ }
+ ]
+ },
+ {
+ "dt": 1594656000,
+ "temp": 28.36,
+ "feels_like": 30.56,
+ "pressure": 1012,
+ "humidity": 69,
+ "dew_point": 22.32,
+ "clouds": 83,
+ "wind_speed": 3.66,
+ "wind_deg": 128,
+ "weather": [
+ {
+ "id": 803,
+ "main": "Clouds",
+ "description": "broken clouds",
+ "icon": "04d"
+ }
+ ]
+ }
+ ],
+ "daily": [
+ {
+ "dt": 1594490400,
+ "sunrise": 1594466108,
+ "sunset": 1594517275,
+ "temp": {
+ "day": 33.38,
+ "min": 27.17,
+ "max": 34.96,
+ "night": 27.17,
+ "eve": 33.35,
+ "morn": 32.69
+ },
+ "feels_like": {
+ "day": 37.27,
+ "night": 30.72,
+ "eve": 39.01,
+ "morn": 36.67
+ },
+ "pressure": 1017,
+ "humidity": 59,
+ "dew_point": 24.27,
+ "wind_speed": 2.97,
+ "wind_deg": 248,
+ "weather": [
+ {
+ "id": 801,
+ "main": "Clouds",
+ "description": "few clouds",
+ "icon": "02d"
+ }
+ ],
+ "clouds": 21,
+ "uvi": 11.94
+ },
+ {
+ "dt": 1594576800,
+ "sunrise": 1594552543,
+ "sunset": 1594603655,
+ "temp": {
+ "day": 34.15,
+ "min": 25.25,
+ "max": 35.89,
+ "night": 26.81,
+ "eve": 33.25,
+ "morn": 25.25
+ },
+ "feels_like": {
+ "day": 36.59,
+ "night": 30.18,
+ "eve": 38.23,
+ "morn": 29.31
+ },
+ "pressure": 1011,
+ "humidity": 54,
+ "dew_point": 23.74,
+ "wind_speed": 4.4,
+ "wind_deg": 236,
+ "weather": [
+ {
+ "id": 500,
+ "main": "Rain",
+ "description": "light rain",
+ "icon": "10d"
+ }
+ ],
+ "clouds": 40,
+ "rain": 1.16,
+ "uvi": 11.61
+ },
+ {
+ "dt": 1594663200,
+ "sunrise": 1594638978,
+ "sunset": 1594690034,
+ "temp": {
+ "day": 29.22,
+ "min": 22.82,
+ "max": 29.22,
+ "night": 26.71,
+ "eve": 28.98,
+ "morn": 22.82
+ },
+ "feels_like": {
+ "day": 34.31,
+ "night": 28.82,
+ "eve": 34.45,
+ "morn": 24.7
+ },
+ "pressure": 1012,
+ "humidity": 72,
+ "dew_point": 23.66,
+ "wind_speed": 0.74,
+ "wind_deg": 287,
+ "weather": [
+ {
+ "id": 804,
+ "main": "Clouds",
+ "description": "overcast clouds",
+ "icon": "04d"
+ }
+ ],
+ "clouds": 85,
+ "uvi": 12.72
+ },
+ {
+ "dt": 1594749600,
+ "sunrise": 1594725414,
+ "sunset": 1594776411,
+ "temp": {
+ "day": 33.67,
+ "min": 24.06,
+ "max": 35.68,
+ "night": 25.94,
+ "eve": 32.24,
+ "morn": 24.06
+ },
+ "feels_like": {
+ "day": 35.84,
+ "night": 27.78,
+ "eve": 35.66,
+ "morn": 27.28
+ },
+ "pressure": 1012,
+ "humidity": 50,
+ "dew_point": 21.95,
+ "wind_speed": 3.45,
+ "wind_deg": 183,
+ "weather": [
+ {
+ "id": 800,
+ "main": "Clear",
+ "description": "clear sky",
+ "icon": "01d"
+ }
+ ],
+ "clouds": 9,
+ "uvi": 11.67
+ },
+ {
+ "dt": 1594836000,
+ "sunrise": 1594811851,
+ "sunset": 1594862786,
+ "temp": {
+ "day": 32.79,
+ "min": 24.61,
+ "max": 35.24,
+ "night": 26.42,
+ "eve": 31.72,
+ "morn": 24.61
+ },
+ "feels_like": {
+ "day": 34.67,
+ "night": 28.57,
+ "eve": 35.11,
+ "morn": 27.47
+ },
+ "pressure": 1014,
+ "humidity": 53,
+ "dew_point": 22.04,
+ "wind_speed": 3.97,
+ "wind_deg": 208,
+ "weather": [
+ {
+ "id": 802,
+ "main": "Clouds",
+ "description": "scattered clouds",
+ "icon": "03d"
+ }
+ ],
+ "clouds": 35,
+ "uvi": 11.55
+ },
+ {
+ "dt": 1594922400,
+ "sunrise": 1594898288,
+ "sunset": 1594949160,
+ "temp": {
+ "day": 33.89,
+ "min": 24.07,
+ "max": 35.5,
+ "night": 26.61,
+ "eve": 32.58,
+ "morn": 24.07
+ },
+ "feels_like": {
+ "day": 35.74,
+ "night": 29.39,
+ "eve": 36.52,
+ "morn": 27.33
+ },
+ "pressure": 1015,
+ "humidity": 49,
+ "dew_point": 22.01,
+ "wind_speed": 3.81,
+ "wind_deg": 225,
+ "weather": [
+ {
+ "id": 802,
+ "main": "Clouds",
+ "description": "scattered clouds",
+ "icon": "03d"
+ }
+ ],
+ "clouds": 29,
+ "uvi": 11.67
+ },
+ {
+ "dt": 1595008800,
+ "sunrise": 1594984726,
+ "sunset": 1595035532,
+ "temp": {
+ "day": 34.95,
+ "min": 24.45,
+ "max": 36.42,
+ "night": 26.97,
+ "eve": 33.81,
+ "morn": 24.45
+ },
+ "feels_like": {
+ "day": 38.11,
+ "night": 29.69,
+ "eve": 38.22,
+ "morn": 26.84
+ },
+ "pressure": 1016,
+ "humidity": 50,
+ "dew_point": 23.17,
+ "wind_speed": 2.94,
+ "wind_deg": 230,
+ "weather": [
+ {
+ "id": 802,
+ "main": "Clouds",
+ "description": "scattered clouds",
+ "icon": "03d"
+ }
+ ],
+ "clouds": 46,
+ "uvi": 11.86
+ },
+ {
+ "dt": 1595095200,
+ "sunrise": 1595071164,
+ "sunset": 1595121903,
+ "temp": {
+ "day": 35.47,
+ "min": 24.4,
+ "max": 37.04,
+ "night": 28.18,
+ "eve": 34.23,
+ "morn": 24.4
+ },
+ "feels_like": {
+ "day": 38.15,
+ "night": 30.61,
+ "eve": 38.67,
+ "morn": 26.95
+ },
+ "pressure": 1015,
+ "humidity": 46,
+ "dew_point": 22.36,
+ "wind_speed": 2.93,
+ "wind_deg": 238,
+ "weather": [
+ {
+ "id": 800,
+ "main": "Clear",
+ "description": "clear sky",
+ "icon": "01d"
+ }
+ ],
+ "clouds": 2,
+ "uvi": 11.67
+ }
+ ]
+}
\ No newline at end of file
diff --git a/weather-widget/weather.lua b/weather-widget/weather.lua
index 5aaf426..4fcdba7 100644
--- a/weather-widget/weather.lua
+++ b/weather-widget/weather.lua
@@ -3,225 +3,456 @@
-- https://openweathermap.org/
--
-- @author Pavel Makhov
--- @copyright 2018 Pavel Makhov
+-- @copyright 2020 Pavel Makhov
-------------------------------------------------
-
-local socket = require("socket")
-local http = require("socket.http")
-local ltn12 = require("ltn12")
+local awful = require("awful")
+local watch = require("awful.widget.watch")
local json = require("json")
local naughty = require("naughty")
local wibox = require("wibox")
local gears = require("gears")
+local beautiful = require("beautiful")
-local path_to_icons = "/usr/share/icons/Arc/status/symbolic/"
+local HOME_DIR = os.getenv("HOME")
+local WIDGET_DIR = HOME_DIR .. '/.config/awesome/awesome-wm-widgets/weather-widget'
+local GET_FORECAST_CMD = [[bash -c "curl -s --show-error -X GET '%s'"]]
+
+local function show_warning(message)
+ naughty.notify {
+ preset = naughty.config.presets.critical,
+ title = 'Weather Widget',
+ text = message
+ }
+end
local weather_widget = {}
+local warning_shown = false
+local notification
+local tooltip = awful.tooltip {
+ mode = 'outside',
+ preferred_positions = {'bottom'}
+}
+
+local weather_popup = awful.popup {
+ ontop = true,
+ visible = false,
+ shape = gears.shape.rounded_rect,
+ border_width = 1,
+ border_color = beautiful.bg_focus,
+ maximum_width = 400,
+ offset = {y = 5},
+ widget = {}
+}
+
+--- Maps openWeatherMap icon name to file name w/o extension
+local icon_map = {
+ ["01d"] = "clear-sky",
+ ["02d"] = "few-clouds",
+ ["03d"] = "scattered-clouds",
+ ["04d"] = "broken-clouds",
+ ["09d"] = "shower-rain",
+ ["10d"] = "rain",
+ ["11d"] = "thunderstorm",
+ ["13d"] = "snow",
+ ["50d"] = "mist",
+ ["01n"] = "clear-sky-night",
+ ["02n"] = "few-clouds-night",
+ ["03n"] = "scattered-clouds-night",
+ ["04n"] = "broken-clouds-night",
+ ["09n"] = "shower-rain-night",
+ ["10n"] = "rain-night",
+ ["11n"] = "thunderstorm-night",
+ ["13n"] = "snow-night",
+ ["50n"] = "mist-night"
+}
+
+--- Return wind direction as a string
+local function to_direction(degrees)
+ -- Ref: https://www.campbellsci.eu/blog/convert-wind-directions
+ if degrees == nil then return "Unknown dir" end
+ local directions = {
+ "N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", "SSW", "SW",
+ "WSW", "W", "WNW", "NW", "NNW", "N"
+ }
+ return directions[math.floor((degrees % 360) / 22.5) + 1]
+end
+
+--- Convert degrees Celsius to Fahrenheit
+local function celsius_to_fahrenheit(c) return c * 9 / 5 + 32 end
+
+-- Convert degrees Fahrenheit to Celsius
+local function fahrenheit_to_celsius(f) return (f - 32) * 5 / 9 end
+
+local function gen_temperature_str(temp, fmt_str, show_other_units, units)
+ local temp_str = string.format(fmt_str, temp)
+ local s = temp_str .. '°' .. (units == 'metric' and 'C' or 'F')
+
+ if (show_other_units) then
+ local temp_conv, units_conv
+ if (units == 'metric') then
+ temp_conv = celsius_to_fahrenheit(temp)
+ units_conv = 'F'
+ else
+ temp_conv = fahrenheit_to_celsius(temp)
+ units_conv = 'C'
+ end
+
+ local temp_conv_str = string.format(fmt_str, temp_conv)
+ s = s .. ' ' .. '(' .. temp_conv_str .. '°' .. units_conv .. ')'
+ end
+ return s
+end
+
+local function uvi_index_color(uvi)
+ local color
+ if uvi >= 0 and uvi < 3 then color = '#A3BE8C'
+ elseif uvi >= 3 and uvi < 6 then color = '#EBCB8B'
+ elseif uvi >= 6 and uvi < 8 then color = '#D08770'
+ elseif uvi >= 8 and uvi < 11 then color = '#BF616A'
+ elseif uvi >= 11 then color = '#B48EAD'
+ end
+
+ return '' .. uvi .. ''
+end
local function worker(args)
local args = args or {}
- local font = args.font or 'Play 9'
- local city = args.city or 'Montreal,ca'
- local api_key = args.api_key or naughty.notify{preset = naughty.config.presets.critical, text = 'OpenweatherMap API key is not set'}
+ --- Validate required parameters
+ if args.coordinates == nil or args.api_key == nil then
+ show_warning('Required parameters are not set: ' ..
+ (args.coordinates == nil and 'coordinates' or '') ..
+ (args.api_key == nil and ', api_key ' or ''))
+ return
+ end
+
+ local coordinates = args.coordinates
+ local api_key = args.api_key
+ local font_name = args.font_name or beautiful.font:gsub("%s%d+$", "")
local units = args.units or 'metric'
+ local time_format_12h = args.time_format_12h
local both_units_widget = args.both_units_widget or false
- local both_units_popup = args.both_units_popup or false
- local position = args.notification_position or "top_right"
+ local show_hourly_forecast = args.show_hourly_forecast
+ local show_daily_forecast = args.show_daily_forecast
+ local icon_pack_name = args.icons or 'weather-underground-icons'
+ local icons_extension = args.icons_extension or '.png'
- local weather_api_url = (
- 'https://api.openweathermap.org/data/2.5/weather'
- .. '?q=' .. city
- .. '&appid=' .. api_key
- .. '&units=' .. units
- )
-
- local icon_widget = wibox.widget {
- {
- id = "icon",
- resize = false,
- widget = wibox.widget.imagebox,
- },
- layout = wibox.container.margin(_, 0, 0, 3),
- set_image = function(self, path)
- self.icon.image = path
- end,
- }
-
- local temp_widget = wibox.widget {
- font = font,
- widget = wibox.widget.textbox,
- }
+ local owm_one_cal_api =
+ ('https://api.openweathermap.org/data/2.5/onecall' ..
+ '?lat=' .. coordinates[1] .. '&lon=' .. coordinates[2] .. '&appid=' .. api_key ..
+ '&units=' .. units .. '&exclude=minutely' ..
+ (show_hourly_forecast == false and ',hourly' or '') ..
+ (show_daily_forecast == false and ',daily' or ''))
weather_widget = wibox.widget {
- icon_widget,
- temp_widget,
+ {
+ {
+ id = 'icon',
+ resize = true,
+ widget = wibox.widget.imagebox
+ },
+ valign = 'center',
+ widget = wibox.container.place,
+ },
+ {
+ id = 'txt',
+ widget = wibox.widget.textbox
+ },
layout = wibox.layout.fixed.horizontal,
- }
-
- --- Maps openWeatherMap icons to Arc icons
- local icon_map = {
- ["01d"] = "weather-clear-symbolic.svg",
- ["02d"] = "weather-few-clouds-symbolic.svg",
- ["03d"] = "weather-clouds-symbolic.svg",
- ["04d"] = "weather-overcast-symbolic.svg",
- ["09d"] = "weather-showers-scattered-symbolic.svg",
- ["10d"] = "weather-showers-symbolic.svg",
- ["11d"] = "weather-storm-symbolic.svg",
- ["13d"] = "weather-snow-symbolic.svg",
- ["50d"] = "weather-fog-symbolic.svg",
- ["01n"] = "weather-clear-night-symbolic.svg",
- ["02n"] = "weather-few-clouds-night-symbolic.svg",
- ["03n"] = "weather-clouds-night-symbolic.svg",
- ["04n"] = "weather-overcast-symbolic.svg",
- ["09n"] = "weather-showers-scattered-symbolic.svg",
- ["10n"] = "weather-showers-symbolic.svg",
- ["11n"] = "weather-storm-symbolic.svg",
- ["13n"] = "weather-snow-symbolic.svg",
- ["50n"] = "weather-fog-symbolic.svg"
- }
-
- --- Return wind direction as a string.
- local function to_direction(degrees)
- -- Ref: https://www.campbellsci.eu/blog/convert-wind-directions
- if degrees == nil then
- return "Unknown dir"
- end
- local directions = {
- "N",
- "NNE",
- "NE",
- "ENE",
- "E",
- "ESE",
- "SE",
- "SSE",
- "S",
- "SSW",
- "SW",
- "WSW",
- "W",
- "WNW",
- "NW",
- "NNW",
- "N",
- }
- return directions[math.floor((degrees % 360) / 22.5) + 1]
- end
-
- -- Convert degrees Celsius to Fahrenheit
- local function celsius_to_fahrenheit(c)
- return c*9/5+32
- end
-
- -- Convert degrees Fahrenheit to Celsius
- local function fahrenheit_to_celsius(f)
- return (f-32)*5/9
- end
-
- local weather_timer = gears.timer({ timeout = 60 })
- local resp
-
- local function gen_temperature_str(temp, fmt_str, show_other_units)
- local temp_str = string.format(fmt_str, temp)
- local s = temp_str .. '°' .. (units == 'metric' and 'C' or 'F')
-
- if (show_other_units) then
- local temp_conv, units_conv
- if (units == 'metric') then
- temp_conv = celsius_to_fahrenheit(temp)
- units_conv = 'F'
+ set_image = function(self, path)
+ self:get_children_by_id('icon')[1].image = path
+ end,
+ set_text = function(self, text)
+ self:get_children_by_id('txt')[1].text = text
+ end,
+ is_ok = function(self, is_ok)
+ if is_ok then
+ self:get_children_by_id('icon')[1]:set_opacity(1)
+ self:get_children_by_id('icon')[1]:emit_signal('widget:redraw_needed')
else
- temp_conv = fahrenheit_to_celsius(temp)
- units_conv = 'C'
+ self:get_children_by_id('icon')[1]:set_opacity(0.2)
+ self:get_children_by_id('icon')[1]:emit_signal('widget:redraw_needed')
end
-
- local temp_conv_str = string.format(fmt_str, temp_conv)
- s = s .. ' ' .. '('.. temp_conv_str .. '°' .. units_conv .. ')'
end
- return s
- end
+ }
- local function error_display(resp_json)
- weather_timer.timeout = math.min(15 * 60, weather_timer.timeout * 2)
- weather_timer:again()
- local err_resp = json.decode(resp_json)
- naughty.notify{
- title = 'Weather Widget Error',
- text = err_resp.message,
- preset = naughty.config.presets.critical,
- }
- end
-
- weather_timer:connect_signal("timeout", function ()
- local resp_json = {}
- local res, status = http.request{
- url=weather_api_url,
- sink=ltn12.sink.table(resp_json),
- -- ref:
- -- http://w3.impa.br/~diego/software/luasocket/old/luasocket-2.0/http.html
- create=function()
- -- ref: https://stackoverflow.com/a/6021774/595220
- local req_sock = socket.tcp()
- -- 't' — overall timeout
- req_sock:settimeout(0.2, 't')
- -- 'b' — block timeout
- req_sock:settimeout(0.001, 'b')
- return req_sock
- end
- }
- if (resp_json ~= nil) then
- resp_json = table.concat(resp_json)
+ local current_weather_widget = wibox.widget {
+ {
+ {
+ {
+ id = 'icon',
+ resize = true,
+ forced_width = 128,
+ forced_height = 128,
+ widget = wibox.widget.imagebox
+ },
+ align = 'center',
+ widget = wibox.container.place
+ },
+ {
+ id = 'description',
+ font = font_name .. ' 10',
+ align = 'center',
+ widget = wibox.widget.textbox
+ },
+ forced_width = 128,
+ layout = wibox.layout.align.vertical
+ },
+ {
+ {
+ {
+ id = 'temp',
+ font = font_name .. ' 48',
+ widget = wibox.widget.textbox
+ },
+ {
+ id = 'feels_like_temp',
+ align = 'center',
+ font = font_name .. ' 9',
+ widget = wibox.widget.textbox
+ },
+ layout = wibox.layout.fixed.vertical
+ },
+ {
+ {
+ id = 'wind',
+ font = font_name .. ' 9',
+ widget = wibox.widget.textbox
+ },
+ {
+ id = 'humidity',
+ font = font_name .. ' 9',
+ widget = wibox.widget.textbox
+ },
+ {
+ id = 'uv',
+ font = font_name .. ' 9',
+ widget = wibox.widget.textbox
+ },
+ expand = 'inside',
+ layout = wibox.layout.align.vertical
+ },
+ spacing = 16,
+ forced_width = 150,
+ layout = wibox.layout.fixed.vertical
+ },
+ forced_width = 300,
+ layout = wibox.layout.flex.horizontal,
+ update = function(self, weather)
+ self:get_children_by_id('icon')[1]:set_image(WIDGET_DIR .. '/icons/' .. icon_pack_name .. '/' .. icon_map[weather.weather[1].icon] .. icons_extension)
+ self:get_children_by_id('temp')[1]:set_text(gen_temperature_str(weather.temp, '%.0f', false, units))
+ self:get_children_by_id('feels_like_temp')[1]:set_text('Feels like ' .. gen_temperature_str(weather.feels_like, '%.0f', false, units))
+ self:get_children_by_id('description')[1]:set_text(weather.weather[1].description)
+ self:get_children_by_id('wind')[1]:set_markup('Wind: ' .. weather.wind_speed .. 'm/s (' .. to_direction(weather.wind_deg) .. ')')
+ self:get_children_by_id('humidity')[1]:set_markup('Humidity: ' .. weather.humidity .. '%')
+ self:get_children_by_id('uv')[1]:set_markup('UV: ' .. uvi_index_color(weather.uvi))
end
+ }
- if (status ~= 200 and resp_json ~= nil and resp_json ~= '') then
- if (not pcall(error_display, resp_json)) then
- naughty.notify{
- title = 'Weather Widget Error',
- text = 'Cannot parse answer',
- preset = naughty.config.presets.critical,
+
+ local daily_forecast_widget = {
+ forced_width = 300,
+ layout = wibox.layout.flex.horizontal,
+ update = function(self, forecast, timezone_offset)
+ local count = #self
+ for i = 0, count do self[i]=nil end
+ for i, day in ipairs(forecast) do
+ if i > 5 then break end
+ local day_forecast = wibox.widget {
+ {
+ text = os.date('%a', tonumber(day.dt) + tonumber(timezone_offset)),
+ align = 'center',
+ font = font_name .. ' 9',
+ widget = wibox.widget.textbox
+ },
+ {
+ {
+ {
+ image = WIDGET_DIR .. '/icons/' .. icon_pack_name .. '/' .. icon_map[day.weather[1].icon] .. icons_extension,
+ resize = true,
+ forced_width = 48,
+ forced_height = 48,
+ widget = wibox.widget.imagebox
+ },
+ align = 'center',
+ layout = wibox.container.place
+ },
+ {
+ text = day.weather[1].description,
+ font = font_name .. ' 8',
+ align = 'center',
+ forced_height = 50,
+ widget = wibox.widget.textbox
+ },
+ layout = wibox.layout.fixed.vertical
+ },
+ {
+ {
+ text = gen_temperature_str(day.temp.day, '%.0f', false, units),
+ align = 'center',
+ font = font_name .. ' 9',
+ widget = wibox.widget.textbox
+ },
+ {
+ text = gen_temperature_str(day.temp.night, '%.0f', false, units),
+ align = 'center',
+ font = font_name .. ' 9',
+ widget = wibox.widget.textbox
+ },
+ layout = wibox.layout.fixed.vertical
+ },
+ spacing = 8,
+ layout = wibox.layout.fixed.vertical
}
+ table.insert(self, day_forecast)
end
- elseif (resp_json ~= nil and resp_json ~= '') then
- resp = json.decode(resp_json)
- icon_widget.image = path_to_icons .. icon_map[resp.weather[1].icon]
- temp_widget:set_text(gen_temperature_str(resp.main.temp, '%.0f', both_units_widget))
- weather_timer.timeout = 60
- weather_timer:again()
end
- end)
- weather_timer:start()
- weather_timer:emit_signal("timeout")
+ }
- --- Notification with weather information. Popups when mouse hovers over the icon
- local notification
- weather_widget:connect_signal("mouse::enter", function()
- notification = naughty.notify{
- icon = path_to_icons .. icon_map[resp.weather[1].icon],
- icon_size=20,
- text =
- '' .. resp.weather[1].main .. ' (' .. resp.weather[1].description .. ')
' ..
- 'Humidity: ' .. resp.main.humidity .. '%
' ..
- 'Temperature: ' .. gen_temperature_str(resp.main.temp, '%.1f',
- both_units_popup) .. '
' ..
- 'Pressure: ' .. resp.main.pressure .. 'hPa
' ..
- 'Clouds: ' .. resp.clouds.all .. '%
' ..
- 'Wind: ' .. resp.wind.speed .. 'm/s (' .. to_direction(resp.wind.deg) .. ')',
- timeout = 5, hover_timeout = 10,
- position = position,
- screen = mouse.screen,
- width = (both_units_popup == true and 210 or 200)
+ local hourly_forecast_graph = wibox.widget {
+ step_width = 12,
+ color = '#EBCB8B',
+ background_color = beautiful.bg_normal,
+ forced_height = 100,
+ forced_width = 300,
+ widget = wibox.widget.graph,
+ set_max_value = function(self, new_max_value)
+ self.max_value = new_max_value
+ end,
+ set_min_value = function(self, new_min_value)
+ self.min_value = new_min_value
+ end
+ }
+
+ local hourly_forecast_widget = {
+ layout = wibox.layout.fixed.vertical,
+ update = function(self, hourly)
+ local hours_below = {
+ id = 'hours',
+ layout = wibox.layout.flex.horizontal
+ }
+ local temp_below = {
+ id = 'temp',
+ forced_width = 300,
+ layout = wibox.layout.flex.horizontal
+ }
+
+ local max_temp = -1000
+ local min_temp = 1000
+ local values = {}
+ for i, hour in ipairs(hourly) do
+ if i > 25 then break end
+ values[i] = hour.temp
+ if max_temp < hour.temp then max_temp = hour.temp end
+ if min_temp > hour.temp then min_temp = hour.temp end
+ if (i - 1) % 5 == 0 then
+ table.insert(hours_below, wibox.widget {
+ text = os.date(time_format_12h and '%I%p' or '%H:00', tonumber(hour.dt)),
+ align = 'center',
+ font = font_name .. ' 9',
+ widget = wibox.widget.textbox
+ })
+ table.insert(temp_below, wibox.widget {
+ markup = '' .. string.format('%.0f', hour.temp) .. '°' .. '',
+ align = 'center',
+ font = font_name .. ' 9',
+ widget = wibox.widget.textbox
+ })
+ end
+ end
+ hourly_forecast_graph:set_max_value(max_temp)
+ hourly_forecast_graph:set_min_value(min_temp * 0.7) -- move graph a bit up
+ for i, value in ipairs(values) do
+ hourly_forecast_graph:add_value(value)
+ end
+
+ local count = #self
+ for i = 0, count do self[i]=nil end
+
+
+ table.insert(self, temp_below)
+ table.insert(self, wibox.widget{
+ {
+ hourly_forecast_graph,
+ reflection = {horizontal = true},
+ widget = wibox.container.mirror
+ },
+ {
+ temp_below,
+ valign = 'bottom',
+ widget = wibox.container.place
+ },
+ id = 'graph',
+ layout = wibox.layout.stack
+ })
+ table.insert(self, hours_below)
+ end
+ }
+
+ local function update_widget(widget, stdout, stderr)
+ if stderr ~= '' then
+ if not warning_shown then
+ show_warning(stderr)
+ warning_shown = true
+ widget:is_ok(false)
+ tooltip:add_to_object(widget)
+
+ widget:connect_signal('mouse::enter', function() tooltip.text = stderr end)
+ end
+ return
+ end
+
+ warning_shown = false
+ tooltip:remove_from_object(widget)
+ widget:is_ok(true)
+
+ local result = json.decode(stdout)
+
+ widget:set_image(WIDGET_DIR .. '/icons/' .. icon_pack_name .. '/' .. icon_map[result.current.weather[1].icon] .. icons_extension)
+ widget:set_text(gen_temperature_str(result.current.temp, '%.0f', both_units_widget, units))
+
+ current_weather_widget:update(result.current)
+
+ local final_widget = {
+ current_weather_widget,
+ spacing = 16,
+ layout = wibox.layout.fixed.vertical
}
- end)
- weather_widget:connect_signal("mouse::leave", function()
- naughty.destroy(notification)
- end)
+ if show_hourly_forecast then
+ hourly_forecast_widget:update(result.hourly)
+ table.insert(final_widget, hourly_forecast_widget)
+ end
+
+ if show_daily_forecast then
+ daily_forecast_widget:update(result.daily, result.timezone_offset)
+ table.insert(final_widget, daily_forecast_widget)
+ end
+
+ weather_popup:setup({
+ {
+ final_widget,
+ margins = 10,
+ widget = wibox.container.margin
+ },
+ bg = beautiful.bg_normal,
+ widget = wibox.container.background
+ })
+ end
+
+ weather_widget:buttons(awful.util.table.join(awful.button({}, 1, function()
+ if weather_popup.visible then
+ weather_popup.visible = not weather_popup.visible
+ else
+ weather_popup:move_next_to(mouse.current_widget_geometry)
+ end
+ end)))
+
+ -- watch('cat /home/pmakhov/.config/awesome/awesome-wm-widgets/weather-widget/weather.json', 5, update_widget, weather_widget)
+ watch(string.format(GET_FORECAST_CMD, owm_one_cal_api), 5, update_widget, weather_widget)
return weather_widget
end
-return setmetatable(weather_widget, { __call = function(_, ...)
- return worker(...)
-end })
+return setmetatable(weather_widget, {__call = function(_, ...) return worker(...) end})