├── .editorconfig
├── .eslintignore
├── .eslintrc
├── .gitattributes
├── .gitignore
├── .npmrc
├── CHANGELOG.md
├── Daily Note.md
├── EXAMPLE.md
├── LICENSE.md
├── README.md
├── RELEASE.md
├── dailyNote.css
├── esbuild.config.mjs
├── getCurrentWeather.ts
├── getForecastWeather.ts
├── images
├── Demo.gif
├── Format1-1.png
├── Format2-1.png
├── Format3-1.png
├── Format3-2.png
├── Format4-1.png
├── Format4-2.png
└── Statusbar-1.png
├── main.js
├── main.ts
├── manifest.json
├── package-lock.json
├── package.json
├── styles.css
├── tsconfig.json
├── version-bump.mjs
└── versions.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | # top-most EditorConfig file
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | end_of_line = lf
7 | insert_final_newline = true
8 | indent_style = space
9 | indent_size = 2
10 | tab_width = 2
11 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | npm node_modules
2 | build
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "parser": "@typescript-eslint/parser",
4 | "env": { "node": true },
5 | "plugins": [
6 | "@typescript-eslint"
7 | ],
8 | "extends": [
9 | "eslint:recommended",
10 | "plugin:@typescript-eslint/eslint-recommended",
11 | "plugin:@typescript-eslint/recommended"
12 | ],
13 | "parserOptions": {
14 | "sourceType": "module"
15 | },
16 | "rules": {
17 | "no-unused-vars": "off",
18 | "@typescript-eslint/no-unused-vars": ["error", { "args": "none" }],
19 | "@typescript-eslint/ban-ts-comment": "off",
20 | "no-prototype-builtins": "off",
21 | "@typescript-eslint/no-empty-function": "off"
22 | }
23 | }
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Set default behavior to automatically normalize line endings.
2 | * text=auto
3 |
4 | # Force batch scripts to always use CRLF line endings so that if a repo is accessed
5 | # in Windows via a file share from Linux, the scripts will work.
6 | *.{cmd,[cC][mM][dD]} text eol=crlf
7 | *.{bat,[bB][aA][tT]} text eol=crlf
8 | *.{ics,[iI][cC][sS]} text eol=crlf
9 |
10 | # Image files are treated as binary by default.
11 | *.jpg binary
12 | *.png binary
13 | *.gif binary
14 |
15 | # Force bash scripts to always use LF line endings so that if a repo is accessed
16 | # in Unix via a file share from Windows, the scripts will work.
17 | *.sh text eol=lf
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # vscode
2 | .vscode
3 |
4 | # Intellij
5 | *.iml
6 | .idea
7 |
8 | # npm
9 | node_modules
10 |
11 | # Don't include the compiled main.js file in the repo.
12 | # They should be uploaded to GitHub releases instead.
13 | # main.js
14 |
15 | # Exclude sourcemaps
16 | *.map
17 |
18 | # obsidian
19 | data.json
20 |
21 | # Exclude macOS Finder (System Explorer) View States
22 | .DS_Store
23 |
24 | # Project notes
25 | .pnotes
26 |
27 | # Project time tracker
28 | .vscode/time_log.json
29 |
30 | # Hotreload - For plugin Development
31 | .hotreload
32 | StartMonitoring.ps1
33 | monitor-log.txt
34 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | tag-version-prefix=""
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 |
4 | ## [1.8.0] 2024-05-26
5 | ### Added
6 | - 5 day forecast
7 | - 33 new placeholders (Times 40 for each time slot - 1320 total)
8 | - 3 complex placeholders `%next12%`, `%next24%`, and `%next48%`
9 | - See the README.md files `Forecast Weather Placeholders` section for details
10 | ### Updated
11 | - README.md
12 | - Added the list of available placeholders
13 | - Updated settings descriptions
14 |
15 | ## [1.7.1] 2024-02-24
16 | ### Fixed
17 | - String settings names were named incorrectly (This effectively reset your defined strings to defaults)
18 | - The original edited strings are still in the data file, this fix just points back to them
19 | - This only affects anyone who had changed the default strings
20 |
21 | ## [1.7.0] 2024-02-18
22 | ### Added
23 | - Settings for Latitude and Longitude
24 | - Please note that API requests by city name have been deprecated although they are still available for use
25 | - Note that setting these will override the location setting
26 | - Use these if you are having issues with getting data returned for the wrong city
27 | - Leave them blank to use the location setting (Not recommended)
28 | - `Geocoding API` - This provides you with the requested cities name, state, country, latitude and longitude
29 | - Settings button for using the `Geocoding API` to retrieve cities name, state, country, latitude and longitude
30 | - This will prompt you with a list of up to 5 locations based on your city name search query
31 | - The settings for location name, latitude and longitude will automatically be updated to the data from your selection
32 | - This should solve any issues with receiving data for the wrong city
33 | - The weather string placeholders `%latitude%` and `%longitude%`
34 | - These values are returned from the API call and are not from the new settings for Latitude and Longitude
35 | - `Air Pollution API` - This provides you with the current Air Quality Index with the corresponding placeholders
36 | - `%aqinumber%` - Numbers 1 to 5 which match with the strings below
37 | - `%aqistring%` - The strings that match the numbers 1 to 5 - 'Good', 'Fair', 'Moderate', 'Poor', 'Very Poor'
38 | - Github repository links in the settings UI
39 | ### Changed
40 | - Wind speed in meters/second now rounded to nearest whole number
41 | ### Fixed
42 | - Now returns an error message on API error Eg. `Error Code 404: city not found` will be displayed as the weather string
43 | ### Updated
44 | - Settings UI improved the layout and descriptions
45 |
46 | ## [1.6.1] 2024-01-13
47 | ### Added
48 | - The files `Daily Note.md` and `dailyNote.css` used in `EXAMPLE.md` file
49 | ### Updated
50 | - The documentation in the `EXAMPLE.md` document has been updated
51 |
52 | ## [1.6.0] 2023-11-07
53 | ### Added
54 | - New macro for wind speed in meters per second %wind-speed-ms%
55 | ### Fixed
56 | - "Failed to fetch weather data TypeError: Cannot read properties of undefined (reading '0')" while entering location in settings
57 |
58 | ## [1.5.1] 2023-04-11
59 | ### Fixed
60 | - Selected exclude folder now excludes files in subfolders as well
61 | - Corrected some typos
62 |
63 | ## [1.5.0] 2023-03-18
64 | ### Added
65 | - Weather Description Emojis
66 | - Macro `%desc-em%` will be replaced with an emoji of the current weather description
67 |
68 | ## [1.4.0] 2023-03-18
69 | ### Added
70 | - Language support for the following languages
71 | - af - Afrikaans
72 | - al - Albanian
73 | - ar - Arabic
74 | - az - Azerbaijani
75 | - bg - Bulgarian
76 | - ca - Catalan
77 | - cz - Czech
78 | - da - Danish
79 | - de - German
80 | - el - Greek
81 | - en - English
82 | - eu - Basque
83 | - fa - Persian (Farsi)
84 | - fi - Finnish
85 | - fr - French
86 | - gl - Galician
87 | - he - Hebrew
88 | - hi - Hindi
89 | - hr - Croatian
90 | - hu - Hungarian
91 | - id - Indonesian
92 | - it - Italian
93 | - ja - Japanese
94 | - kr - Korean
95 | - la - Latvian
96 | - lt - Lithuanian
97 | - mk - Macedonian
98 | - no - Norwegian
99 | - nl - Dutch
100 | - pl - Polish
101 | - pt - Portuguese
102 | - pt_br - Português Brasil
103 | - ro - Romanian
104 | - ru - Russian
105 | - sv - Swedish
106 | - sk - Slovak
107 | - sl - Slovenian
108 | - sp - Spanish
109 | - sr - Serbian
110 | - th - Thai
111 | - tr - Turkish
112 | - ua - uk Ukrainian
113 | - vi - Vietnamese
114 | - zh_cn - Chinese Simplified
115 | - zh_tw - Chinese Traditional
116 | - zu - Zulu
117 |
118 | ## [1.3.0] 2023-03-10
119 | ### Fixed
120 | - removed obsidian from ID
121 |
122 | ## [1.2.0] 2023-03-02
123 | ### Added
124 | - Cloud coverage %clouds% (Percentage)
125 | - Rain past 1 hour %rain1h% (in millimeters)
126 | - Rain past 3 hours %rain3h% (in millimeters)
127 | - Snow past 1 hour %snow1h% (in millimeters)
128 | - Snow past 3 hours %snow3h% (in millimeters)
129 | - Precipitation past 1 hour %precipitation1h% (in millimeters - Rain or Snow)
130 | - Precipitation past 3 hours %precipitation3h% (in millimeters - Rain or Snow)
131 |
132 | ## [1.1.0] 2023-02-07
133 | ### Added
134 | - Now runs on mobile (Display weather in statusbar is disabled)
135 | - New setting `Exclude Folder` which will exclude a folder from automatic template strings replacement (set this to your templates folder so it does not replace the strings in your template)
136 |
137 | ### Fixed
138 | - Dynamic weather in DIV's should be shown more reliably
139 |
140 | ## [1.0.0] 2022-12-22
141 | - Initial release
142 |
--------------------------------------------------------------------------------
/Daily Note.md:
--------------------------------------------------------------------------------
1 | ---
2 | cssclasses: daily, calendar
3 | banner: "![[daily-note-banner.jpg]]"
4 | banner_x: 0.5
5 | banner_y: 0.3
6 | obsidianUIMode: preview
7 | title: <%tp.file.title%>
8 | created: <%tp.date.now()%>
9 | modified: <%tp.date.now()%>
10 | last_reviewed: <%tp.date.now()%>
11 | author: "William McKeever"
12 | source:
13 | status:
14 | webclip: false
15 | tags: [daily_note]
16 | aliases: []
17 | description: "Daily Note"
18 | ---
19 |
%weather3%
20 |
21 |
22 | | <%moment(tp.file.title, 'YYYY-MM-DD').subtract(3, 'd').format('ddd')%> | <%moment(tp.file.title, 'YYYY-MM-DD').subtract(2, 'd').format('ddd')%> | <%moment(tp.file.title, 'YYYY-MM-DD').subtract(1, 'd').format('ddd')%> | <%moment(tp.file.title, 'YYYY-MM-DD').format('ddd')%> | <%moment(tp.file.title, 'YYYY-MM-DD').add(1, 'd').format('ddd')%> | <%moment(tp.file.title, 'YYYY-MM-DD').add(2, 'd').format('ddd')%> | <%moment(tp.file.title, 'YYYY-MM-DD').add(3, 'd').format('ddd')%> |
23 | |:------:|:------:|:------:|:------:|:------:|:------:|:----------:|
24 | | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').subtract(17, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').subtract(17, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').subtract(16, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').subtract(16, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').subtract(15, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').subtract(15, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').subtract(14, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').subtract(14, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').subtract(13, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').subtract(13, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').subtract(12, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').subtract(12, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').subtract(11, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').subtract(11, 'd').format('DD')%>]] |
25 | | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').subtract(10, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').subtract(10, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').subtract(9, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').subtract(9, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').subtract(8, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').subtract(8, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').subtract(7, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').subtract(7, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').subtract(6, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').subtract(6, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').subtract(5, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').subtract(5, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').subtract(4, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').subtract(4, 'd').format('DD')%>]] |
26 | | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').subtract(3, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').subtract(3, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').subtract(2, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').subtract(2, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').subtract(1, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').subtract(1, 'd').format('DD')%>]] | ==**[[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').format('DD')%>]]**== | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').add(1, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').add(1, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').add(2, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').add(2, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').add(3, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').add(3, 'd').format('DD')%>]] |
27 | | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').add(4, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').add(4, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').add(5, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').add(5, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').add(6, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').add(6, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').add(7, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').add(7, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').add(8, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').add(8, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').add(9, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').add(9, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').add(10, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').add(10, 'd').format('DD')%>]] |
28 | | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').add(11, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').add(11, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').add(12, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').add(12, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').add(13, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').add(13, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').add(14, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').add(14, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').add(15, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').add(15, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').add(16, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').add(16, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').add(17, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').add(17, 'd').format('DD')%>]] |
29 |
30 | > ###### [[<% tp.date.now("YYYY-MM-DD", -1, tp.file.title, "YYYY-MM-DD") %>|⬅]] <% tp.file.title %> [[<% tp.date.now("YYYY-MM-DD", 1, tp.file.title, "YYYY-MM-DD") %>|➡]]
31 | > ##### 🔹 <% tp.date.now("dddd 🔹 MMMM Do 🔹 YYYY", 0, tp.file.title, "YYYY-MM-DD") %> 🔹
32 | >
33 | <%tp.web.daily_quote()%>
34 |
35 | ---
36 |
37 | ## Todo Today
38 | - [ ] `button-review` Review recent notes.
39 |
40 | ## Achievments for Today
41 |
42 | ## Research
43 |
44 | ## Ideas
45 |
46 | ## Quick notes
47 |
48 |
--------------------------------------------------------------------------------
/EXAMPLE.md:
--------------------------------------------------------------------------------
1 | # My Daily Note Example
2 |
3 | ## Demonstration Information
4 |
5 | This is an example of how I add the weather information into my Daily Notes. The recorded temperature is the permanently recorded temperature available at the time of my Daily Notes creation. When I hover over that it shows me the current temperature information. This is achieved through the use of CSS. All the code involved is also listed in this document and copies have been added to the repository.
6 |
7 | Please note the web site `https://api.quotable.io/` which is used by the Templater plugin was down at the time I made the example gif so please just ignore the error message. Also the lines for the calendar are very long and may not be displayed properly in your markdown viewer or on Github.
8 |
9 | It makes use of the following plugins, [banners](https://github.com/noatpad/obsidian-banners), [templater](https://github.com/SilentVoid13/Templater), [Buttons](https://github.com/shabegom/buttons) and of course this plugin [OpenWeather](https://github.com/willasm/obsidian-open-weather).
10 |
11 | ### Screenshot
12 |
13 | 
14 |
15 | ## [My Daily Note Template](Daily%20Note.md)
16 |
17 | ```markdown
18 | ---
19 | cssclasses: daily, calendar
20 | banner: "![[daily-note-banner.jpg]]"
21 | banner_x: 0.5
22 | banner_y: 0.3
23 | obsidianUIMode: preview
24 | title: <%tp.file.title%>
25 | created: <%tp.date.now()%>
26 | modified: <%tp.date.now()%>
27 | last_reviewed: <%tp.date.now()%>
28 | author: "William McKeever"
29 | source:
30 | status:
31 | webclip: false
32 | tags: [daily_note]
33 | aliases: []
34 | description: "Daily Note"
35 | ---
36 | %weather3%
37 |
38 |
39 | | <%moment(tp.file.title, 'YYYY-MM-DD').subtract(3, 'd').format('ddd')%> | <%moment(tp.file.title, 'YYYY-MM-DD').subtract(2, 'd').format('ddd')%> | <%moment(tp.file.title, 'YYYY-MM-DD').subtract(1, 'd').format('ddd')%> | <%moment(tp.file.title, 'YYYY-MM-DD').format('ddd')%> | <%moment(tp.file.title, 'YYYY-MM-DD').add(1, 'd').format('ddd')%> | <%moment(tp.file.title, 'YYYY-MM-DD').add(2, 'd').format('ddd')%> | <%moment(tp.file.title, 'YYYY-MM-DD').add(3, 'd').format('ddd')%> |
40 | |:------:|:------:|:------:|:------:|:------:|:------:|:----------:|
41 | | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').subtract(17, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').subtract(17, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').subtract(16, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').subtract(16, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').subtract(15, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').subtract(15, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').subtract(14, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').subtract(14, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').subtract(13, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').subtract(13, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').subtract(12, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').subtract(12, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').subtract(11, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').subtract(11, 'd').format('DD')%>]] |
42 | | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').subtract(10, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').subtract(10, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').subtract(9, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').subtract(9, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').subtract(8, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').subtract(8, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').subtract(7, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').subtract(7, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').subtract(6, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').subtract(6, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').subtract(5, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').subtract(5, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').subtract(4, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').subtract(4, 'd').format('DD')%>]] |
43 | | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').subtract(3, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').subtract(3, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').subtract(2, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').subtract(2, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').subtract(1, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').subtract(1, 'd').format('DD')%>]] | ==**[[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').format('DD')%>]]**== | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').add(1, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').add(1, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').add(2, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').add(2, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').add(3, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').add(3, 'd').format('DD')%>]] |
44 | | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').add(4, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').add(4, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').add(5, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').add(5, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').add(6, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').add(6, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').add(7, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').add(7, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').add(8, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').add(8, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').add(9, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').add(9, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').add(10, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').add(10, 'd').format('DD')%>]] |
45 | | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').add(11, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').add(11, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').add(12, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').add(12, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').add(13, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').add(13, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').add(14, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').add(14, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').add(15, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').add(15, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').add(16, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').add(16, 'd').format('DD')%>]] | [[Daily/<%moment(tp.file.title, 'YYYY-MM-DD').add(17, 'd').format('YYYY-MM-DD')%>\|<%moment(tp.file.title, 'YYYY-MM-DD').add(17, 'd').format('DD')%>]] |
46 |
47 | > ###### [[<% tp.date.now("YYYY-MM-DD", -1, tp.file.title, "YYYY-MM-DD") %>|⬅]] <% tp.file.title %> [[<% tp.date.now("YYYY-MM-DD", 1, tp.file.title, "YYYY-MM-DD") %>|➡]]
48 | > ##### 🔹 <% tp.date.now("dddd 🔹 MMMM Do 🔹 YYYY", 0, tp.file.title, "YYYY-MM-DD") %> 🔹
49 |
50 | <%tp.web.daily_quote()%>
51 |
52 | ---
53 |
54 | ## Todo Today
55 | - [ ] `button-review` Review recent notes.
56 |
57 | ## Achievments for Today
58 |
59 | ## Research
60 |
61 | ## Ideas
62 |
63 | ## Quick notes
64 |
65 | ```
66 |
67 | ## [My Daily Notes CSS (dailyNote.css)](dailyNote.css)
68 |
69 | ```css
70 | /*====================*/
71 | /* Daily Note Styling */
72 | /*====================*/
73 | .daily {
74 | padding-left: 25px !important;
75 | padding-right: 25px !important;
76 | padding-top: 20px !important;
77 | }
78 |
79 | /* Transition Effect Weather One */
80 | .weather_current_1,
81 | .weather_historical_1 {
82 | transition: 0.3s;
83 | }
84 |
85 | /* Transition Effect Weather Two */
86 | .weather_current_2,
87 | .weather_historical_2 {
88 | transition: 0.3s;
89 | }
90 |
91 | /* Transition Effect Weather Three */
92 | .weather_current_3,
93 | .weather_historical_3 {
94 | transition: 0.3s;
95 | }
96 |
97 | /* Transition Effect Weather Four */
98 | .weather_current_4,
99 | .weather_historical_4 {
100 | transition: 0.3s;
101 | }
102 |
103 | /* Historical & Current weather One, Two, Three and Four common settings */
104 | .weather_historical_1, .weather_current_1, .weather_historical_2, .weather_current_2, .weather_historical_3, .weather_current_3, .weather_historical_4, .weather_current_4 {
105 | display: flex;
106 | float: left;
107 | clear: left;
108 | align-items: center;
109 | top: 45px;
110 | left: 55px;
111 | white-space: pre;
112 | position: absolute;
113 | font-family: monospace;
114 | font-size: 14pt !important;
115 | margin: 10px 5px;
116 | padding: 10px 20px;
117 | border-radius: 20px;
118 | box-shadow: 3px 3px 2px #414654;
119 | cursor: pointer;
120 | }
121 |
122 | /* Historical weather at top of the document over banner */
123 | .weather_historical_1, .weather_historical_2, .weather_historical_3, .weather_historical_4 {
124 | color: #d0dce9;
125 | background-color: #0d3d56;
126 | }
127 |
128 | /* Current weather at top of the document over banner */
129 | .weather_current_1, .weather_current_2, .weather_current_3, .weather_current_4 {
130 | color: #c4caa5;
131 | background-color: #133e2c;
132 | opacity: 0;
133 | }
134 |
135 | /* Show Current weather One on hover */
136 | .weather_current_1:hover {
137 | opacity: 1;
138 | }
139 |
140 | /* Show Current weather Two on hover */
141 | .weather_current_2:hover {
142 | opacity: 1;
143 | }
144 |
145 | /* Show Current weather Three on hover */
146 | .weather_current_3:hover {
147 | opacity: 1;
148 | }
149 |
150 | /* Show Current weather Four on hover */
151 | .weather_current_4:hover {
152 | opacity: 1;
153 | }
154 |
155 | /* Daily Note Name H6 Styling (YYYY-MM-DD) */
156 | .daily h6 {
157 | font-size: 1.75em;
158 | color: #4cbf90;
159 | border-width: 1px;
160 | padding-bottom: 3px;
161 | text-align: center;
162 | }
163 |
164 | /* Daily Note Date H5 Styling (🔹 Thursday 🔹 10th November 🔹 2022 🔹) */
165 | .daily h5 {
166 | font-size: 1.25em;
167 | color: #23a49d;
168 | border-width: 1px;
169 | padding-bottom: 3px;
170 | text-align: center;
171 | }
172 |
173 | /* Hide the frontmatter for Daily Notes
174 | .daily .frontmatter-container {
175 | display: none;
176 | }
177 | */
178 |
179 | /*==============================*/
180 | /* Display Daily Notes Calendar */
181 | /*==============================*/
182 | .calendar table {
183 | top: 30px;
184 | right: 30px;
185 | position: absolute;
186 | background-color: #0d3d56;
187 | border-radius: 10px;
188 | }
189 |
190 | .calendar thead {
191 | background-color: #147980;
192 | }
193 |
194 | .calendar th {
195 | color: goldenrod !important;
196 | }
197 |
198 | .calendar table th:first-of-type {
199 | border-top-left-radius: 6px;
200 | }
201 | .calendar table th:last-of-type {
202 | border-top-right-radius: 6px;
203 | }
204 |
205 | .calendar .internal-link {
206 | color: rgb(215, 146, 27);
207 | }
208 |
209 | .calendar .internal-link.is-unresolved {
210 | color: silver;
211 | }
212 |
213 | .calendar td strong {
214 | border-radius: 50%;
215 | padding: 3px;
216 | border: 2px solid #229ecf;
217 | color: black !important;
218 | }
219 |
220 | .calendar mark {
221 | /* color: #fd0707 !important; */
222 | background: #0d3d56;
223 | }
224 |
225 | .calendar strong .internal-link{
226 | color: #23bca5 !important;
227 | /* background: #0d3d56; */
228 | }
229 |
230 | ```
231 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License Copyright (c) 2022 William McKeever
2 |
3 | Permission is hereby granted,
4 | free of charge, to any person obtaining a copy of this software and associated
5 | documentation files (the "Software"), to deal in the Software without
6 | restriction, including without limitation the rights to use, copy, modify, merge,
7 | publish, distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to the
9 | following conditions:
10 |
11 | The above copyright notice and this permission notice
12 | (including the next paragraph) shall be included in all copies or substantial
13 | portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
18 | EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
19 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # OpenWeather Plugin for Obsidian
2 |
3 | > IMPORTANT NOTICE - THIS PLUGIN IS NOW DISCONTINUED!
4 | >
5 | > (Continue reading though - I have good news to announce as well)
6 | >
7 | >
8 | > Open Weather has discontinued API 2.5 on which this plugin is based. They do offer API 3.0 which is free, but it requires a credit card to subscribe to the API. I'm not interested in giving some company a way to automatically charge me for something in the future when/if they decide to. I'm pretty sure that would never actually happen, but I can say that I am positively sure it can not happen if they do not have my credit card info. Also if they did start charging the users of this plugin (around 10,000 users) for some unforseen reason, that would have a lot of people very unhappy with me and that would be unpleasant to say the least...
9 | >
10 | > One other point is that there are enough differences between the two API's that it would basically require a complete re-write of the plugin which leads into my good news... I have very nearly completed a new weather plugin based on the [Visual Crossing Weather API](https://www.visualcrossing.com/weather-api). It is free to subscribe to and does not require anything other than your email address. You can [sign up here](https://www.visualcrossing.com/sign-up). It offers many great features that were not included with Open Weather. For example, 15 complete forecast days while Open Weather only had 5 and for 21 hours out of every 24, day 1 and 5 did not return the full data for those days. VCW API also returns hourly data for every one of those 15 days while OW only returns the data in 3 hour blocks. Other nice features that VCW API offers and OW API is missing is weather alerts, data is returned in local time rather than GMT (No conversions needed). The API documentation is also much better and it has an active support forum. Overall it is just a nicer API to work with.
11 | >
12 | > If you want to try the new plugin now you can find the [Visual Crossing Weather plugin here](https://github.com/willasm/vc-weather). It has some big improvements over the old plugin. It now has 5 locations you can get the weather data for as opposed to just one. I have doubled the number of weather templates from 4 to 8. There are now 2 statusbar strings which can be cycled every 30 seconds. The defaults are the first one has info on todays weather, the second displays info for tommorows weather. Note while this is still a work in progress it has almost every feature working now (Two feature that I still want to add are not yet completed). The documentation is also basically non-existent at the moment although I do have the complete list of current macros created (nearly 4000 already) and it is displayed in an easy to read table format. I have uploaded the main.js file as well if you would like to do a manual install or you could use the BRAT plugin to do the work for you. Anyone willing to Beta test it would be a huge help if they could provide any feedback.
13 | >
14 | > Please note that the API was supposed to be discontinued 3 weeks ago, but as right now it is still returnig data. This could stop at any time!!! It is fortunate that it has continued this long as it has given me the time to write the new plugin. I will request the removal of this plugin from community plugins list when the API stops working or I have added the new plugin to community plugins list.
15 | >
16 | > Thank you to all the plugins users since its creation, William McKeever
17 | >
18 |
19 |
20 |
21 |
22 | ## Features
23 | - Display current weather in the statusbar
24 | - Insert current weather into your documents
25 | - Four customizable weather strings available
26 | - Customizable statusbar weather string
27 | - [Template support](#template-support) for automatic weather insertion into your new documents
28 | - [DIV support](#div-support) for dynamic weather
29 | - 5 Day forecast available
30 |
31 | ## Default Weather Strings with Example Screenshots
32 |
33 | #### **Statusbar String**
34 | `' | %desc% | Current Temp: %temp%°C | Feels Like: %feels%°C | '`
35 |
36 | 
37 |
38 | #### **Weather String One**
39 | `'%desc% • Current Temp: %temp%°C • Feels Like: %feels%°C\n'`
40 |
41 | 
42 |
43 | #### **Weather String Two**
44 | `'%name%: %dateMonth4% %dateDay2% - %timeH2%:%timeM% %ampm1%\nCurrent Temp: %temp%°C • Feels Like: %feels%°C\nWind: %wind-speed% km/h from the %wind-dir%^ with gusts up to %wind-gust% km/h^\nSunrise: %sunrise% • Sunset: %sunset%\n'`
45 |
46 | 
47 |
48 | #### **Weather String Three**
49 | `'%icon% %dateMonth4% %dateDay2% %dateYear1% • %timeH2%:%timeM% %ampm1% • %desc%
Recorded Temp: %temp% • Felt like: %feels%
Wind: %wind-speed% km/h from the %wind-dir%^ with gusts up to %wind-gust% km/h^
Sunrise: %sunrise% • Sunset: %sunset%'`
50 |
51 | 
52 |
53 | #### **Weather String Four**
54 | `'%icon% %dateMonth4% %dateDay2% %dateYear1% • %timeH2%:%timeM% %ampm1% • %desc%
Current Temp: %temp% • Feels like: %feels%
Wind: %wind-speed% km/h from the %wind-dir%^ with gusts up to %wind-gust% km/h^
Sunrise: %sunrise% • Sunset: %sunset%'`
55 |
56 | 
57 |
58 | #### **Format Strings Three & Four within DIV's and styled wih CSS**
59 |
60 | Format String Three...
61 |
62 | 
63 |
64 | Format String Four...
65 |
66 | 
67 |
68 | Note: The `\n`'s are not required when editing these in the settings. Simply enter a `return` to add a new line and the `\n` will be added to the saved settings file. The `
`'s in string formats 3 & 4 are required for use in HTML.
69 |
70 | See [EXAMPLE.md](EXAMPLE.md) for a demonstration of how I use this in my Daily Template.
71 |
72 | ## Settings
73 |
74 | #### **OpenWeather API Key**
75 | Enter your OpenWeather API Key here (Required)
76 |
77 | A free OpenWeather API key is required for the plugin to work.
78 | Go to https://openweathermap.org to register and get a key.
79 | Direct link to signup page https://home.openweathermap.org/users/sign_up.
80 |
81 | Note: You will need to verify your email address, then your API key will be emailed to you. The key itself may take a couple of hours before it is activated. All this information will be included in the email they send to you.
82 |
83 | #### **Use Geocoding API to get location (recommended)**
84 | This Geocoding API returns the requested locations name, state, country, latitude and longitude allowing you to choose the correct location. This is beneficial in cases where your city has a common name shared by other cities. To use this, enter your cities name in the text field and press the `Get location` button. You will be prompted with a list of up to 5 locations to choose from.
85 |
86 | #### **Enter Location**
87 | Note: It is recommended to use the new `Use Geocoding API to get location (recommended)` command to fill this in for you.
88 |
89 | Enter your city's name (This setting is required unless latitude and longitude are defined)
90 | Note: If you are getting the wrong data try including your state and country codes. They can be entered as {city name},{state code},{country code}. Eg. South Bend, WA, US (The commas are required). If you are still having issues getting the correct data, then use the Latitude and Longitude settings instead.
91 |
92 | #### **Enter Latitude**
93 | Note: It is recommended to use the new `Use Geocoding API to get location (recommended)` command to fill this in for you.
94 |
95 | Enter your city's latitude (Setting Latitude and Longitude will override the Location setting)
96 |
97 | Please note that API requests by city name have been deprecated although they are still available for use. The preferred method is to use latitude and longitude.
98 |
99 | #### **Enter Longitude**
100 | Note: It is recommended to use the new `Use Geocoding API to get location (recommended)` command to fill this in for you.
101 |
102 | Enter your city's longitude (Setting Latitude and Longitude will override the Location setting)
103 |
104 | Please note that API requests by city name have been deprecated although they are still available for use. The preferred method is to use latitude and longitude.
105 |
106 | #### **Units of Measurement**
107 | Metric, Imperial and Standard units can be selected here. (Note: Standard is in Kelvin, not really useful in most cases)
108 |
109 | #### **Language**
110 | Supported languages available (46 languages total)
111 | Note: This only applies to text that is returned by the Open Weather API. This does not change the text in the defined weather strings. If you want the text in the default weather strings in another language you will need to edit them directly in the settings.
112 |
113 | #### **Exclude Folder**
114 | Folder to exclude from automatic [Template](#template-support) strings replacement. This should be set to your vaults template folder. The exclusion includes any subfolders within the selected folder.
115 |
116 | #### **Weather Strings **
117 | Define your weather strings here (4 strings are available + 1 for the statusbar)
118 |
119 | _Tip: These strings can contain anything you want, not just weather information._
120 |
121 | #### **Show Weather in Statusbar** Note: This will not be displayed on mobile app
122 | Toggle display of the current weather in the statusbar on or off
123 |
124 | #### **Weather String Format Statusbar** Note: This will not be displayed on mobile app
125 | Define your statusbar weather string here
126 |
127 | #### **Update Frequency**
128 | Time interval to update the weather displayed in the statusbar and [DIV's](#div-support) (1, 5, 10, 15, 20, 30 or 60 minutes)
129 |
130 | ## Weather String Placeholders
131 | These macros contained within the weather string will be replaced with the appropiate data.
132 |
133 | ### Current Weather Placeholders
134 | - Weather Description Text `%desc%`
135 | - Weather Description Emoji `%desc-em%`
136 | - Weather Icon `%icon%` - See note below
137 | - Weather Icon Double sized `%icon2x%` - See note below
138 | - Current Temperature `%temp%`
139 | - Feels Like `%feels%`
140 | - Temperature Min `%tempmin%`
141 | - Temperature Max `%tempmax%`
142 | - Air Pressure `%pressure%`
143 | - Humidity `%humidity%`
144 | - Pressure at Sea Level `%pressure-sl%`
145 | - Pressure at Ground Level `%pressure-gl%`
146 | - Visibility `%visibility%`
147 | - Wind Speed `%wind-speed%` - km/h for Metric, mph for Imperial
148 | - Wind Speed `%wind-speed-ms%` - m/s (Meters per second)
149 | - Wind Direction `%wind-dir%` - Eg. Northwest
150 | - Wind Gust `%wind-gust%` - See note below
151 | - Cloud coverage `%clouds%` (Percentage)
152 | - Rain past 1 hour `%rain1h%` (in millimeters)
153 | - Rain past 3 hours `%rain3h%` (in millimeters)
154 | - Snow past 1 hour `%snow1h%` (in millimeters)
155 | - Snow past 3 hours `%snow3h%` (in millimeters)
156 | - Precipitation past 1 hour `%precipitation1h%` (in millimeters - Rain or Snow)
157 | - Precipitation past 3 hours `%precipitation3h%` (in millimeters - Rain or Snow)
158 | - Sunrise `%sunrise%` - 08:30:30 (24 hour format)
159 | - Sunset `%sunset%` - 19:30:30 (24 hour format)
160 | - City Name `%name%` - Eg. Edmonton
161 | - Latitude `%latitude%` - Eg. 46.66
162 | - Longitude `%longitude%` - Eg. -123.80
163 | - Air Quality Index as number `%aqinumber%` - 1 to 5 (Order matches the strings list)
164 | - Air Quality Index as string `%aqistring%` - 'Good', 'Fair', 'Moderate', 'Poor', 'Very Poor' (Order matches the numbers list)
165 | - (Date & Time) - The date & time of the most recent data information that OpenWeather API has available
166 | - year1 `%dateYear1%` - 2022
167 | - year2 `%dateYear2%` - 22
168 | - month1 `%dateMonth1%` - 1
169 | - month2 `%dateMonth2%` - 01
170 | - month3 `%dateMonth3%` - Jan
171 | - month4 `%dateMonth4%` - January
172 | - date1 `%dateDay1%` - 2
173 | - date2 `%dateDay2%` - 02
174 | - ampm1 `%ampm1%` - "AM"
175 | - ampm2 `%ampm2%` - "am"
176 | - hour1 `%timeH1%` - 23 (24 hour)
177 | - hour2 `%timeH2%` - 1 (12 hour)
178 | - min `%timeM%` - 05
179 | - sec `%timeS%` - 05
180 |
181 | ### Forecast Weather Placeholders
182 | Note: The 5 day forecast is returned in 3 hour increments (total of 40 data objects). This means that the data returned by the API does not start at 12:00 am tommorow, but for the next 3 hour slice of available data. This means that only once a day can you get the full forecast for 5 days (just before midnight). At all other times you will recieve partial data for today and partial data for the last day. You will need to account for this when defining your weather strings. To make it easier for you I have included the placeholders `%next12%`, `%next24%`, and `%next48%` (see placeholder example) and will add more in the future.
183 |
184 | Note: The placeholders represent the 3 hour forecast objects and are numbered 00, 01, 02, ... 39 in the placeholders.
185 |
186 | - `%fyear_00%` to `%fyear_39%` - Forecast Year Eg. 2024
187 | - `%fmonth_00%` to `%fmonth_39%` - Forecast Month Eg. 05
188 | - `%fdate_00%` to `%fdate_39%` - Forecast Date Eg. 26
189 | - `%fhours_00%` to `%fhours_39%` - Forecast Hours Eg. 18
190 | - `%fmins_00%` to `%fmins_39%` - Forecast Minutes Eg. 00 (will always be 00)
191 | - `%fsecs_00%` to `%fsecs_39%` - Forecast Seconds Eg. 00 (will always be 00)
192 | - `%dt_localtime_00%` to `%dt_localtime_39%` - Forecast local date and time string Eg. 2024-05-26 18:00:00
193 | - `%d_localtime_00%` to `%d_localtime_39%` - Forecast local date string Eg. 2024-05-26
194 | - `%ds_localtime_00%` to `%ds_localtime_39%` - Forecast local date short string Eg. 05-26
195 | - `%t_localtime_00%` to `%t_localtime_39%` - Forecast local time string Eg. 18:00:00
196 | - `%ts_localtime_00%` to `%ts_localtime_39%` - Forecast local time short string Eg. 18:00
197 | - `%ftemp_00%` to `%ftemp_39%` - Forecast temperature Eg. 15
198 | - `%ffeels_00%` to `%ffeels_39%` - Forecast feels like temperature Eg. 14
199 | - `%fclouds_00%` to `%fclouds_39%` - Forecast cloud coverage Eg. 99
200 | - `%fpop_00%` to `%fpop_39%` - Probability of precipitation Eg. 100
201 | - `%fpod_00%` to `%fpod_39%` - Part of the day (n - night, d - day) Eg. d
202 | - `%fvis_00%` to `%fvis_39%` - Visibility in feet Eg. 10000
203 | - `%fhum_00%` to `%fhum_39%` - Humidity percentage Eg. 70
204 | - `%ftempmax_00%` to `%ftempmax_39%` - Maximum temperature Eg. 16
205 | - `%ftempmin_00%` to `%ftempmin_39%` - Minimum temperature Eg. 15
206 | - `%fground_00%` to `%fground_39%` - Ground level pressure in millibarsEg. 928
207 | - `%fsea_00%` to `%fsea_39%` - Sea level pressure in millibarsEg. 1007
208 | - `%fdesc_00%` to `%fdesc_39%` - Weather description Eg. Light Rain
209 | - `%fmaindesc_00%` to `%fmaindesc_39%` - Weather main description Eg. Rain
210 | - `%fdescem_00%` to `%fdescem_39%` - Weather description emoji Eg. 🌧️
211 | - `%ficonurl_00%` to `%ficonurl_39%` - Weather icon URL Eg. https://openweathermap.org/img/wn/10d.png
212 | - `%ficonurl2x_00%` to `%ficonurl2x_39%` - Weather icon URL double size Eg. https://openweathermap.org/img/wn/10d@2x.png
213 | - `%fwindspeed_00%` to `%fwindspeed_39%` - Wind speed in miles per hour Eg. 7
214 | - `%fwindspeedms_00%` to `%fwindspeedms_39%` - Wind speed in meters per second Eg. 2
215 | - `%fwinddeg_00%` to `%fwinddeg_39%` - Wind direction in degrees Eg. 198
216 | - `%fwinddir_00%` to `%fwinddir_39%` - Wind direction Eg. South
217 | - `%fwindgust_00%` to `%fwindgust_39%` - Wind gusts miles per hour Eg. 7
218 | - `%fwindgustms_00%` to `%fwindgustms_39%` - Wind gusts meters per second Eg. 2
219 | - `%next12%` - Forecast list for the next 12 hours Eg...
220 | - 05-26 - 18:00 🌧️ Light Rain Temp: 15 Feels Like: 14
221 | - 05-26 - 21:00 🌧️ Light Rain Temp: 14 Feels Like: 13
222 | - 05-27 - 00:00 ☁️ Overcast Clouds Temp: 12 Feels Like: 11
223 | - 05-27 - 03:00 🌧️ Light Rain Temp: 9 Feels Like: 8
224 | - `%next24%` - Forecast list for the next 24 hours Eg...
225 | - 05-26 - 18:00 🌧️ Light Rain Temp: 15 Feels Like: 14
226 | - 05-26 - 21:00 🌧️ Light Rain Temp: 14 Feels Like: 13
227 | - 05-27 - 00:00 ☁️ Overcast Clouds Temp: 12 Feels Like: 11
228 | - 05-27 - 03:00 🌧️ Light Rain Temp: 9 Feels Like: 8
229 | - 05-27 - 06:00 🌧️ Scattered Clouds Temp: 8 Feels Like: 7
230 | - 05-27 - 09:00 🌥️ Scattered Clouds Temp: 13 Feels Like: 12
231 | - 05-27 - 12:00 🌥️ Broken Clouds Temp: 18 Feels Like: 17
232 | - 05-27 - 15:00 ☁️ Overcast Clouds Temp: 20 Feels Like: 17
233 | - `%next48%` - Forecast list for the next 48 hours Eg...
234 | - 05-26 - 18:00 🌧️ Light Rain Temp: 15 Feels Like: 14
235 | - 05-26 - 21:00 🌧️ Light Rain Temp: 14 Feels Like: 13
236 | - 05-27 - 00:00 ☁️ Overcast Clouds Temp: 12 Feels Like: 11
237 | - 05-27 - 03:00 🌧️ Light Rain Temp: 9 Feels Like: 8
238 | - 05-27 - 06:00 🌧️ Scattered Clouds Temp: 8 Feels Like: 7
239 | - 05-27 - 09:00 🌥️ Scattered Clouds Temp: 13 Feels Like: 12
240 | - 05-27 - 12:00 🌥️ Broken Clouds Temp: 18 Feels Like: 17
241 | - 05-27 - 15:00 ☁️ Overcast Clouds Temp: 20 Feels Like: 17
242 | - 05-27 - 18:00 ☁️ Overcast Clouds Temp: 20 Feels Like: 20
243 | - 05-27 - 21:00 ☁️ Overcast Clouds Temp: 15 Feels Like: 15
244 | - 05-28 - 00:00 ☁️ Overcast Clouds Temp: 14 Feels Like: 13
245 | - 05-28 - 03:00 ☁️ Overcast Clouds Temp: 12 Feels Like: 11
246 | - 05-28 - 06:00 ☁️ Overcast Clouds Temp: 10 Feels Like: 9
247 | - 05-28 - 09:00 🌥️ Broken Clouds Temp: 14 Feels Like: 14
248 | - 05-28 - 12:00 🌥️ Broken Clouds Temp: 19 Feels Like: 18
249 | - 05-28 - 15:00 ☁️ Overcast Clouds Temp: 21 Feels Like: 20
250 |
251 | - ### Weather Placeholder notes
252 | - `%icon%` and `%icon2x%` - This is replaced with the image tag `
` This is more useful if it is embedded inside a [div](#div-support) code block.
253 |
254 | - `%wind-gust%` This data is only returned by the API if the condition exists. To make this display the string data only when it exists you can surround it with the caret symbols.
255 |
256 | - For example: `Winds %wind-speed% km/h^ with gusts up to %wind-gust% km/h^`
257 | - With wind gust data this will convert to: `Winds 10 km/h with gusts up to 20 km/h`
258 | - Without wind gust data this will convert to: `Winds 10 km/h` (The text surrounded by carets will be removed)
259 |
260 | ## OpenWeather Plugin Commands
261 |
262 | #### All these commands are available from the command palette and the ribbon icon
263 |
264 | - `OpenWeather: Insert weather string one` - Inserts Weather String One into the current document.
265 | - `OpenWeather: Insert weather string two` - Inserts Weather String Two into the current document.
266 | - `OpenWeather: Insert weather string three` - Inserts Weather String Three into the current document.
267 | - `OpenWeather: Insert weather string four` - Inserts Weather String Four into the current document.
268 | - Note: If text is selected in the current document when these commands are run, it will be replaced by the inserted weather string.
269 | - `OpenWeather: Replace template string` - This will replace all occurences of the strings, `%weather1%`, `%weather2%`, `%weather3%` and `%weather4%` with the corresponding defined weather strings. See also [Template support](#template-support)
270 |
271 | ## Template support
272 | You can place the following strings in your templates and when creating a new document using the template, they will automatically be replaced with the corresponding weather strings.
273 |
274 | - `%weather1%` - Inserts weather string One
275 | - `%weather2%` - Inserts weather string Two
276 | - `%weather3%` - Inserts weather string Three
277 | - `%weather4%` - Inserts weather string Four
278 |
279 | ## DIV support
280 | You can insert the following DIV inside your documents to provide dynamic weather which is updated at the frequency set in the [settings _Update Frequency_](#update-frequency) setting. The `weather_historical_3` is the static temperature at the time the document is created and the `weather_current_1` is dynamic. See [EXAMPLE.md](EXAMPLE.md) for a demonstration of how I use these in my Daily Template.
281 |
282 |
283 | ```html
284 | %weather3%
285 |
286 | ```
287 |
288 | You can use the following class's to insert the corresponding weather strings
289 |
290 | - "weather_current_1" Inserts dynamic weather string One
291 | - "weather_current_2" Inserts dynamic weather string Two
292 | - "weather_current_3" Inserts dynamic weather string Three
293 | - "weather_current_4" Inserts dynamic weather string Four
294 |
295 | and...
296 |
297 | - "weather_historical_1" Inserts static weather string One
298 | - "weather_historical_2" Inserts static weather string Two
299 | - "weather_historical_3" Inserts static weather string Three
300 | - "weather_historical_4" Inserts static weather string Four
301 |
302 |
--------------------------------------------------------------------------------
/RELEASE.md:
--------------------------------------------------------------------------------
1 | # Release Notes
2 |
11 |
12 |
13 | ## [1.8.0] 2024-05-26
14 | ### Added
15 | - 5 day forecast
16 | - 33 new placeholders (Times 40 for each time slot - 1320 total)
17 | - 3 complex placeholders `%next12%`, `%next24%`, and `%next48%`
18 | - See the README.md files `Forecast Weather Placeholders` section for details
19 | ### Updated
20 | - README.md
21 | - Added the list of available placeholders
22 | - Updated settings descriptions
23 |
24 | For a full list of changes, please see the projects [Changelog](CHANGELOG.md) file.
25 |
26 | I hope you enjoy using the Plugin, and if you find any bugs, or would like to see a certain feature added, please feel free to contact me.
27 |
28 | Enjoy! William
29 |
--------------------------------------------------------------------------------
/dailyNote.css:
--------------------------------------------------------------------------------
1 | /*====================*/
2 | /* Daily Note Styling */
3 | /*====================*/
4 | .daily {
5 | padding-left: 25px !important;
6 | padding-right: 25px !important;
7 | padding-top: 20px !important;
8 | }
9 |
10 | /* Transition Effect Weather One */
11 | .weather_current_1,
12 | .weather_historical_1 {
13 | transition: 0.3s;
14 | }
15 |
16 | /* Transition Effect Weather Two */
17 | .weather_current_2,
18 | .weather_historical_2 {
19 | transition: 0.3s;
20 | }
21 |
22 | /* Transition Effect Weather Three */
23 | .weather_current_3,
24 | .weather_historical_3 {
25 | transition: 0.3s;
26 | }
27 |
28 | /* Transition Effect Weather Four */
29 | .weather_current_4,
30 | .weather_historical_4 {
31 | transition: 0.3s;
32 | }
33 |
34 | /* Historical & Current weather One, Two, Three and Four common settings */
35 | .weather_historical_1, .weather_current_1, .weather_historical_2, .weather_current_2, .weather_historical_3, .weather_current_3, .weather_historical_4, .weather_current_4 {
36 | display: flex;
37 | float: left;
38 | clear: left;
39 | align-items: center;
40 | top: 45px;
41 | left: 55px;
42 | white-space: pre;
43 | position: absolute;
44 | font-family: monospace;
45 | font-size: 14pt !important;
46 | margin: 10px 5px;
47 | padding: 10px 20px;
48 | border-radius: 20px;
49 | box-shadow: 3px 3px 2px #414654;
50 | cursor: pointer;
51 | }
52 |
53 | /* Historical weather at top of the document over banner */
54 | .weather_historical_1, .weather_historical_2, .weather_historical_3, .weather_historical_4 {
55 | color: #d0dce9;
56 | background-color: #0d3d56;
57 | }
58 |
59 | /* Current weather at top of the document over banner */
60 | .weather_current_1, .weather_current_2, .weather_current_3, .weather_current_4 {
61 | color: #c4caa5;
62 | background-color: #133e2c;
63 | opacity: 0;
64 | }
65 |
66 | /* Show Current weather One on hover */
67 | .weather_current_1:hover {
68 | opacity: 1;
69 | }
70 |
71 | /* Show Current weather Two on hover */
72 | .weather_current_2:hover {
73 | opacity: 1;
74 | }
75 |
76 | /* Show Current weather Three on hover */
77 | .weather_current_3:hover {
78 | opacity: 1;
79 | }
80 |
81 | /* Show Current weather Four on hover */
82 | .weather_current_4:hover {
83 | opacity: 1;
84 | }
85 |
86 | /* Daily Note Name H6 Styling (YYYY-MM-DD) */
87 | .daily h6 {
88 | font-size: 1.75em;
89 | color: #4cbf90;
90 | border-width: 1px;
91 | padding-bottom: 3px;
92 | text-align: center;
93 | }
94 |
95 | /* Daily Note Date H5 Styling (🔹 Thursday 🔹 10th November 🔹 2022 🔹) */
96 | .daily h5 {
97 | font-size: 1.25em;
98 | color: #23a49d;
99 | border-width: 1px;
100 | padding-bottom: 3px;
101 | text-align: center;
102 | }
103 |
104 | /* Hide the frontmatter for Daily Notes
105 | .daily .frontmatter-container {
106 | display: none;
107 | }
108 | */
109 |
110 | /*==============================*/
111 | /* Display Daily Notes Calendar */
112 | /*==============================*/
113 | .calendar table {
114 | top: 30px;
115 | right: 30px;
116 | position: absolute;
117 | background-color: #0d3d56;
118 | border-radius: 10px;
119 | }
120 |
121 | .calendar thead {
122 | background-color: #147980;
123 | }
124 |
125 | .calendar th {
126 | color: goldenrod !important;
127 | }
128 |
129 | .calendar table th:first-of-type {
130 | border-top-left-radius: 6px;
131 | }
132 | .calendar table th:last-of-type {
133 | border-top-right-radius: 6px;
134 | }
135 |
136 | .calendar .internal-link {
137 | color: rgb(215, 146, 27);
138 | }
139 |
140 | .calendar .internal-link.is-unresolved {
141 | color: silver;
142 | }
143 |
144 | .calendar td strong {
145 | border-radius: 50%;
146 | padding: 3px;
147 | border: 2px solid #229ecf;
148 | color: black !important;
149 | }
150 |
151 | .calendar mark {
152 | /* color: #fd0707 !important; */
153 | background: #0d3d56;
154 | }
155 |
156 | .calendar strong .internal-link{
157 | color: #23bca5 !important;
158 | /* background: #0d3d56; */
159 | }
160 |
161 |
--------------------------------------------------------------------------------
/esbuild.config.mjs:
--------------------------------------------------------------------------------
1 | import esbuild from "esbuild";
2 | import process from "process";
3 | import builtins from 'builtin-modules'
4 |
5 | const banner =
6 | `/*
7 | THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
8 | if you want to view the source, please visit the github repository of this plugin
9 | */
10 | `;
11 |
12 | const prod = (process.argv[2] === 'production');
13 |
14 | esbuild.build({
15 | banner: {
16 | js: banner,
17 | },
18 | entryPoints: ['main.ts'],
19 | bundle: true,
20 | external: [
21 | 'obsidian',
22 | 'electron',
23 | '@codemirror/autocomplete',
24 | '@codemirror/collab',
25 | '@codemirror/commands',
26 | '@codemirror/language',
27 | '@codemirror/lint',
28 | '@codemirror/search',
29 | '@codemirror/state',
30 | '@codemirror/view',
31 | '@lezer/common',
32 | '@lezer/highlight',
33 | '@lezer/lr',
34 | ...builtins],
35 | format: 'cjs',
36 | watch: !prod,
37 | target: 'es2018',
38 | logLevel: "info",
39 | sourcemap: prod ? false : 'inline',
40 | treeShaking: true,
41 | outfile: 'main.js',
42 | }).catch(() => process.exit(1));
43 |
--------------------------------------------------------------------------------
/getCurrentWeather.ts:
--------------------------------------------------------------------------------
1 | // • getCurrentWeather - Returns Air Qulity Index From API •
2 | export default async function getCurrentWeather(key:any, latitude:any, longitude:any, language:any, units:any, format:any) {
3 | let weatherData;
4 | let weatherString;
5 | let url;
6 | let aqiNumber;
7 | let aqiString;
8 |
9 | // • Get Air Quality Index From API •
10 | let urlAQI = `https://api.openweathermap.org/data/2.5/air_pollution?lat=${latitude}&lon=${longitude}&appid=${key}`
11 | if (latitude.length > 0 && longitude.length > 0 && key.length > 0) {
12 | let reqAQI = await fetch(urlAQI);
13 | let jsonAQI = await reqAQI.json();
14 | if (jsonAQI.cod) {
15 | aqiNumber = 0;
16 | aqiString = "Air Quality Index is Not Available";
17 | } else {
18 | const aqiStringsArr = ['Good', 'Fair', 'Moderate', 'Poor', 'Very Poor'];
19 | aqiNumber = jsonAQI.list[0].main.aqi;
20 | aqiString = aqiStringsArr[aqiNumber-1]
21 | };
22 | } else {
23 | aqiNumber = 0;
24 | aqiString = "Air Quality Index is Not Available";
25 | };
26 |
27 | // • Get Current Weather From API •
28 | if (latitude.length > 0 && longitude.length > 0) {
29 | url = `https://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&lang=${language}&appid=${key}&units=${units}`;
30 | } else {
31 | url = `https://api.openweathermap.org/data/2.5/weather?q=${location}&lang=${language}&appid=${key}&units=${units}`;
32 | };
33 | let req = await fetch(url);
34 | let json = await req.json();
35 | //console.log('json:', json);
36 | if (json.cod != 200) {
37 | weatherString = "Error Code "+json.cod+": "+json.message;
38 | return weatherString;
39 | };
40 |
41 | // • Get Current Weather Data We Want •
42 | let conditions = json.weather[0].description;
43 | let id = json.weather[0].id;
44 | let conditionsEm = '';
45 | if (id > 199 && id < 300) {
46 | conditionsEm = '⛈️';
47 | };
48 | if (id > 299 && id < 500) {
49 | conditionsEm = '🌦️';
50 | };
51 | if (id > 499 && id < 600) {
52 | conditionsEm = '🌧️';
53 | };
54 | if (id > 599 && id < 700) {
55 | conditionsEm = '❄️';
56 | };
57 | if (id > 699 && id < 800) {
58 | conditionsEm = '🌫️';
59 | };
60 | if (id == 771) {
61 | conditionsEm = '🌀';
62 | };
63 | if (id == 781) {
64 | conditionsEm = '🌪️';
65 | };
66 | if (id == 800) {
67 | conditionsEm = '🔆';
68 | };
69 | if (id > 800 && id < 804) {
70 | conditionsEm = '🌥️';
71 | };
72 | if (id == 804) {
73 | conditionsEm = '☁️';
74 | };
75 | conditions = conditions.replace(/^\w|\s\w/g, (c: string) => c.toUpperCase());
76 | let iconName = json.weather[0].icon;
77 | const iconApi = await fetch('https://openweathermap.org/img/wn/' + iconName + '.png');
78 | let iconUrl = iconApi.url;
79 | const iconApi2x = await fetch('https://openweathermap.org/img/wn/' + iconName + '@2x.png');
80 | let iconUrl2x = iconApi2x.url;
81 | let temp = json.main.temp;
82 | temp = Math.round(temp);
83 | let feelsLike = json.main.feels_like;
84 | feelsLike = Math.round(feelsLike);
85 | let tempMin = json.main.temp_min;
86 | tempMin = Math.round(tempMin);
87 | let tempMax = json.main.temp_max;
88 | tempMax = Math.round(tempMax);
89 | let pressure = json.main.pressure;
90 | let humidity = json.main.humidity;
91 | let seaLevel = json.main.sea_level;
92 | let groundLevel = json.main.grnd_level;
93 | let visibility = json.visibility;
94 | // Winds
95 | let windSpeed = json.wind.speed;
96 | let windSpeedms = json.wind.speed;
97 | if (this.units == "metric") {
98 | windSpeed = Math.round(windSpeed*3.6);
99 | windSpeedms = Math.round(windSpeedms);
100 | } else {
101 | windSpeed = Math.round(windSpeed);
102 | }
103 | let windDirection = json.wind.deg;
104 | const directions = ['North', 'Northeast', 'East', 'Southeast', 'South', 'Southwest', 'West', 'Northwest'];
105 | windDirection = directions[Math.round(windDirection / 45) % 8];
106 | let windGust = json.wind.gust;
107 | if (windGust != undefined) {
108 | if (this.units == "metric") {
109 | windGust = Math.round(windGust*3.6);
110 | } else {
111 | windGust = Math.round(windGust);
112 | }
113 | } else {
114 | windGust = "N/A";
115 | }
116 |
117 | // Cloud cover
118 | let clouds = json.clouds.all;
119 | // Precipitation
120 | let rain1h;
121 | let rain3h;
122 | let snow1h;
123 | let snow3h;
124 | let precipitation1h;
125 | let precipitation3h;
126 | // Precipitation - Rain
127 | if (json.rain != undefined) {
128 | let rainObj = json.rain;
129 | let keys = Object.keys(rainObj);
130 | let values = Object.values(rainObj);
131 | if (keys[0] === "1h") {
132 | rain1h = values[0];
133 | } else if (keys[0] === "3h") {
134 | rain3h = values[0];
135 | }
136 | if (keys.length > 1) {
137 | if (keys[1] === "1h") {
138 | rain1h = values[1];
139 | } else if (keys[1] === "3h") {
140 | rain3h = values[1];
141 | }
142 | }
143 | } else {
144 | rain1h = 0;
145 | rain3h = 0;
146 | }
147 | if (rain1h === undefined) {rain1h = 0};
148 | if (rain3h === undefined) {rain3h = 0};
149 | // Precipitation - Snow
150 | if (json.snow != undefined) {
151 | let snowObj = json.snow;
152 | let keys = Object.keys(snowObj);
153 | let values = Object.values(snowObj);
154 | if (keys[0] === "1h") {
155 | snow1h = values[0];
156 | } else if (keys[0] === "3h") {
157 | snow3h = values[0];
158 | }
159 | if (keys.length > 1) {
160 | if (keys[1] === "1h") {
161 | snow1h = values[1];
162 | } else if (keys[1] === "3h") {
163 | snow3h = values[1];
164 | }
165 | }
166 | } else {
167 | snow1h = 0;
168 | snow3h = 0;
169 | }
170 | if (snow1h === undefined) {snow1h = 0};
171 | if (snow3h === undefined) {snow3h = 0};
172 | precipitation1h = rain1h || snow1h;
173 | precipitation3h = rain3h || snow3h;
174 |
175 | // Date/Time of last weather update from API
176 | let dt = json.dt;
177 | let a = new Date(dt * 1000);
178 | const months1 = ["1","2","3","4","5","6","7","8","9","10","11","12"];
179 | const months2 = ["01","02","03","04","05","06","07","08","09","10","11","12"];
180 | const months3 = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
181 | const months4 = ["January","February","March","April","May","June","July","August","September","October","November","December"];
182 | let year1 = a.getFullYear();
183 | let year2str = String(year1).slice(-2);
184 | let year2 = Number(year2str);
185 | let month1 = months1[a.getMonth()];
186 | let month2 = months2[a.getMonth()];
187 | let month3 = months3[a.getMonth()];
188 | let month4 = months4[a.getMonth()];
189 | let date1 = a.getDate();
190 | let date2 = a.getDate() < 10 ? "0" + a.getDate() : a.getDate();
191 | let ampm1 = "AM";
192 | let ampm2 = "am";
193 | if (a.getHours() > 11) {
194 | ampm1 = "PM";
195 | ampm2 = "pm"
196 | }
197 | let hour1 = a.getHours();
198 | let hour2 = a.getHours();
199 | if (a.getHours() > 12) {
200 | hour2 = a.getHours() - 12;
201 | }
202 | if (a.getHours() == 0) {
203 | hour1 = 12;
204 | hour2 = 12;
205 | }
206 | let min = a.getMinutes() < 10 ? "0" + a.getMinutes() : a.getMinutes();
207 | let sec = a.getSeconds() < 10 ? "0" + a.getSeconds() : a.getSeconds();
208 | // Sunrise and Sunset times
209 | let sr = json.sys.sunrise;
210 | let b = new Date(sr * 1000);
211 | let srhour = b.getHours() < 10 ? '0' + b.getHours() : b.getHours();
212 | let srmin = b.getMinutes() < 10 ? '0' + b.getMinutes() : b.getMinutes();
213 | let srsec = b.getSeconds() < 10 ? '0' + b.getSeconds() : b.getSeconds();
214 | let sunrise = srhour + ':' + srmin + ':' + srsec;
215 | let ss = json.sys.sunset;
216 | let c = new Date(ss * 1000);
217 | let sshour = c.getHours() < 10 ? '0' + c.getHours() : c.getHours();
218 | let ssmin = c.getMinutes() < 10 ? '0' + c.getMinutes() : c.getMinutes();
219 | let sssec = c.getSeconds() < 10 ? '0' + c.getSeconds() : c.getSeconds();
220 | let sunset = sshour + ':' + ssmin + ':' + sssec;
221 | // Location Name
222 | let name = json.name;
223 | // Latitude and Longitude
224 | let lat = json.coord.lat;
225 | let lon = json.coord.lon;
226 |
227 | // getWeather - Create weather data object
228 | weatherData = {
229 | "status": "ok",
230 | "conditions": conditions,
231 | "conditionsEm": conditionsEm,
232 | "icon": iconUrl,
233 | "icon2x": iconUrl2x,
234 | "temp": temp,
235 | "feelsLike": feelsLike,
236 | "tempMin": tempMin,
237 | "tempMax": tempMax,
238 | "pressure": pressure,
239 | "humidity": humidity,
240 | "seaLevel": seaLevel,
241 | "groundLevel": groundLevel,
242 | "visibility": visibility,
243 | "windSpeed": windSpeed,
244 | "windSpeedms": windSpeedms,
245 | "windDirection": windDirection,
246 | "windGust": windGust,
247 | "clouds": clouds,
248 | "rain1h": rain1h,
249 | "rain3h": rain3h,
250 | "snow1h": snow1h,
251 | "snow3h": snow3h,
252 | "precipitation1h": precipitation1h,
253 | "precipitation3h": precipitation3h,
254 | "year1": year1,
255 | "year2": year2,
256 | "month1": month1,
257 | "month2": month2,
258 | "month3": month3,
259 | "month4": month4,
260 | "date1": date1,
261 | "date2": date2,
262 | "ampm1": ampm1,
263 | "ampm2": ampm2,
264 | "hour1": hour1,
265 | "hour2": hour2,
266 | "min": min,
267 | "sec": sec,
268 | "sunrise": sunrise,
269 | "sunset": sunset,
270 | "name": name,
271 | "latitude": lat,
272 | "longitude": lon,
273 | "aqiNum": aqiNumber,
274 | "aqiStr": aqiString
275 | }
276 |
277 | // getWeather - Create Formatted weather string
278 | weatherString = format.replace(/%desc%/gmi, weatherData.conditions);
279 | weatherString = weatherString.replace(/%desc-em%/gmi, weatherData.conditionsEm);
280 | weatherString = weatherString.replace(/%icon%/gmi, `
`);
281 | weatherString = weatherString.replace(/%icon2x%/gmi, `
`);
282 | weatherString = weatherString.replace(/%temp%/gmi, weatherData.temp);
283 | weatherString = weatherString.replace(/%feels%/gmi, weatherData.feelsLike);
284 | weatherString = weatherString.replace(/%tempmin%/gmi, weatherData.tempMin);
285 | weatherString = weatherString.replace(/%tempmax%/gmi, weatherData.tempMax);
286 | weatherString = weatherString.replace(/%pressure%/gmi, weatherData.pressure);
287 | weatherString = weatherString.replace(/%humidity%/gmi, weatherData.humidity);
288 | weatherString = weatherString.replace(/%pressure-sl%/gmi, weatherData.seaLevel);
289 | weatherString = weatherString.replace(/%pressure-gl%/gmi, weatherData.groundLevel);
290 | weatherString = weatherString.replace(/%visibility%/gmi, weatherData.visibility);
291 | weatherString = weatherString.replace(/%wind-speed%/gmi, weatherData.windSpeed);
292 | weatherString = weatherString.replace(/%wind-speed-ms%/gmi, weatherData.windSpeedms);
293 | weatherString = weatherString.replace(/%wind-dir%/gmi, weatherData.windDirection);
294 | if (weatherData.windGust == "N/A") {
295 | weatherString = weatherString.replace(/\^.+\^/gmi, "");
296 | } else {
297 | weatherString = weatherString.replace(/%wind-gust%/gmi, weatherData.windGust);
298 | weatherString = weatherString.replace(/\^(.+)\^/gmi, "$1");
299 | }
300 | weatherString = weatherString.replace(/%clouds%/gmi, `${weatherData.clouds}`);
301 | weatherString = weatherString.replace(/%rain1h%/gmi, `${weatherData.rain1h}`);
302 | weatherString = weatherString.replace(/%rain3h%/gmi, `${weatherData.rain3h}`);
303 | weatherString = weatherString.replace(/%snow1h%/gmi, `${weatherData.snow1h}`);
304 | weatherString = weatherString.replace(/%snow3h%/gmi, `${weatherData.snow3h}`);
305 | weatherString = weatherString.replace(/%precipitation1h%/gmi, `${weatherData.precipitation1h}`);
306 | weatherString = weatherString.replace(/%precipitation3h%/gmi, `${weatherData.precipitation3h}`);
307 | weatherString = weatherString.replace(/%dateYear1%/gmi, `${weatherData.year1}`);
308 | weatherString = weatherString.replace(/%dateYear2%/gmi, `${weatherData.year2}`);
309 | weatherString = weatherString.replace(/%dateMonth1%/gmi, `${weatherData.month1}`);
310 | weatherString = weatherString.replace(/%dateMonth2%/gmi, `${weatherData.month2}`);
311 | weatherString = weatherString.replace(/%dateMonth3%/gmi, `${weatherData.month3}`);
312 | weatherString = weatherString.replace(/%dateMonth4%/gmi, `${weatherData.month4}`);
313 | weatherString = weatherString.replace(/%dateDay1%/gmi, `${weatherData.date1}`);
314 | weatherString = weatherString.replace(/%dateDay2%/gmi, `${weatherData.date2}`);
315 | weatherString = weatherString.replace(/%ampm1%/gmi, `${weatherData.ampm1}`);
316 | weatherString = weatherString.replace(/%ampm2%/gmi, `${weatherData.ampm2}`);
317 | weatherString = weatherString.replace(/%timeH1%/gmi, `${weatherData.hour1}`);
318 | weatherString = weatherString.replace(/%timeH2%/gmi, `${weatherData.hour2}`);
319 | weatherString = weatherString.replace(/%timeM%/gmi, `${weatherData.min}`);
320 | weatherString = weatherString.replace(/%timeS%/gmi, `${weatherData.sec}`);
321 | weatherString = weatherString.replace(/%sunrise%/gmi, `${weatherData.sunrise}`);
322 | weatherString = weatherString.replace(/%sunset%/gmi, `${weatherData.sunset}`);
323 | weatherString = weatherString.replace(/%name%/gmi, `${weatherData.name}`);
324 | weatherString = weatherString.replace(/%latitude%/gmi, `${weatherData.latitude}`);
325 | weatherString = weatherString.replace(/%longitude%/gmi, `${weatherData.longitude}`);
326 | weatherString = weatherString.replace(/%aqinumber%/gmi, `${weatherData.aqiNum}`);
327 | weatherString = weatherString.replace(/%aqistring%/gmi, `${weatherData.aqiStr}`);
328 |
329 | return weatherString;
330 | }
331 |
332 | // // • getCardinalDirection - Converts the wind direction in degrees to text and returns the string value •
333 | // getCardinalDirection(angle: number) {
334 | // const directions = ['North', 'Northeast', 'East', 'Southeast', 'South', 'Southwest', 'West', 'Northwest'];
335 | // return directions[Math.round(angle / 45) % 8];
336 | // }
337 |
--------------------------------------------------------------------------------
/images/Demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/willasm/obsidian-open-weather/f457f6c6f02e1d5f1455176e6b3f04fbd1ec67d6/images/Demo.gif
--------------------------------------------------------------------------------
/images/Format1-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/willasm/obsidian-open-weather/f457f6c6f02e1d5f1455176e6b3f04fbd1ec67d6/images/Format1-1.png
--------------------------------------------------------------------------------
/images/Format2-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/willasm/obsidian-open-weather/f457f6c6f02e1d5f1455176e6b3f04fbd1ec67d6/images/Format2-1.png
--------------------------------------------------------------------------------
/images/Format3-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/willasm/obsidian-open-weather/f457f6c6f02e1d5f1455176e6b3f04fbd1ec67d6/images/Format3-1.png
--------------------------------------------------------------------------------
/images/Format3-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/willasm/obsidian-open-weather/f457f6c6f02e1d5f1455176e6b3f04fbd1ec67d6/images/Format3-2.png
--------------------------------------------------------------------------------
/images/Format4-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/willasm/obsidian-open-weather/f457f6c6f02e1d5f1455176e6b3f04fbd1ec67d6/images/Format4-1.png
--------------------------------------------------------------------------------
/images/Format4-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/willasm/obsidian-open-weather/f457f6c6f02e1d5f1455176e6b3f04fbd1ec67d6/images/Format4-2.png
--------------------------------------------------------------------------------
/images/Statusbar-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/willasm/obsidian-open-weather/f457f6c6f02e1d5f1455176e6b3f04fbd1ec67d6/images/Statusbar-1.png
--------------------------------------------------------------------------------
/main.ts:
--------------------------------------------------------------------------------
1 | import { App, Editor, MarkdownView, Notice, Plugin, PluginSettingTab, Setting, TextAreaComponent, TAbstractFile, TFolder, SuggestModal, Platform, FileSystemAdapter } from 'obsidian';
2 | import getCurrentWeather from './getCurrentWeather';
3 | import getForecastWeather from './getForecastWeather';
4 | import * as fs from 'fs';
5 | import * as path from 'path';
6 | //const path = require('path');
7 |
8 | let displayErrorMsg = true;
9 |
10 | interface OpenWeatherSettings {
11 | location: string;
12 | latitude: string;
13 | longitude: string;
14 | key: string;
15 | units: string;
16 | language: string;
17 | excludeFolder: string;
18 | weatherFormat1: string;
19 | weatherFormat2: string;
20 | weatherFormat3: string;
21 | weatherFormat4: string;
22 | statusbarActive: boolean;
23 | weatherFormatSB: string;
24 | statusbarUpdateFreq: string;
25 | }
26 |
27 | const DEFAULT_SETTINGS: OpenWeatherSettings = {
28 | location: '',
29 | latitude: '',
30 | longitude: '',
31 | key: '',
32 | units: 'metric',
33 | language: 'en',
34 | excludeFolder: '',
35 | weatherFormat1: '%desc% • Current Temp: %temp%°C • Feels Like: %feels%°C\n',
36 | weatherFormat2: '%name%: %dateMonth4% %dateDay2% - %timeH2%:%timeM% %ampm1%\nCurrent Temp: %temp%°C • Feels Like: %feels%°C\nWind: %wind-speed% km/h from the %wind-dir%^ with gusts up to %wind-gust% km/h^\nSunrise: %sunrise% • Sunset: %sunset%\n',
37 | weatherFormat3: `%icon% %dateMonth4% %dateDay2% %dateYear1% • %timeH2%:%timeM% %ampm1% • %desc%
38 | Recorded Temp: %temp% • Felt like: %feels%
39 | Wind: %wind-speed% km/h from the %wind-dir%^ with gusts up to %wind-gust% km/h^
40 | Sunrise: %sunrise% • Sunset: %sunset%`,
41 | weatherFormat4: `%icon% %dateMonth4% %dateDay2% %dateYear1% • %timeH2%:%timeM% %ampm1% • %desc%
42 | Current Temp: %temp% • Feels like: %feels%
43 | Wind: %wind-speed% km/h from the %wind-dir%^ with gusts up to %wind-gust% km/h^
44 | Sunrise: %sunrise% • Sunset: %sunset%`,
45 | // weatherFormat3: '%icon% %dateMonth4% %dateDay2% %dateYear1% • %timeH2%:%timeM% %ampm1% • %desc%
Recorded Temp: %temp% • Felt like: %feels%
Wind: %wind-speed% km/h from the %wind-dir%^ with gusts up to %wind-gust% km/h^
Sunrise: %sunrise% • Sunset: %sunset%',
46 | // weatherFormat4: '%icon% %dateMonth4% %dateDay2% %dateYear1% • %timeH2%:%timeM% %ampm1% • %desc%
Current Temp: %temp% • Feels like: %feels%
Wind: %wind-speed% km/h from the %wind-dir%^ with gusts up to %wind-gust% km/h^
Sunrise: %sunrise% • Sunset: %sunset%',
47 | statusbarActive: true,
48 | weatherFormatSB: ' | %desc% | Current Temp: %temp%°C | Feels Like: %feels%°C | ',
49 | statusbarUpdateFreq: "15"
50 | }
51 |
52 | // ╭──────────────────────────────────────────────────────────────────────────────╮
53 | // │ ● Class FormatWeather ● │
54 | // │ │
55 | // │ • Get Current Weather From OpenWeather API and Return a Formatted String • │
56 | // ╰──────────────────────────────────────────────────────────────────────────────╯
57 | class FormatWeather {
58 | location: String;
59 | latitude: String;
60 | longitude: String;
61 | key: string
62 | units: string
63 | language: string
64 | format: string
65 |
66 | constructor(location: string, latitude: string, longitude: string, key: string, units: string, language: string, format: string) {
67 | this.location = location;
68 | this.latitude = latitude;
69 | this.longitude = longitude;
70 | this.key = key;
71 | this.units = units;
72 | this.language = language;
73 | this.format = format;
74 | }
75 |
76 | // • getWeather - Get the weather data from the OpenWeather API •
77 | async getWeather():Promise {
78 | let weatherStr = await getCurrentWeather(this.key, this.latitude, this.longitude, this.language, this.units, this.format);
79 | let forecastWeatherStr = await getForecastWeather(this.key, this.latitude, this.longitude, this.language, this.units, weatherStr);
80 | return(forecastWeatherStr);
81 | }
82 |
83 | // • getWeatherString - Returns a formatted weather string •
84 | async getWeatherString() {
85 | try {
86 | let weatherString = await this.getWeather();
87 | return weatherString;
88 | } catch (error) {
89 | console.log('Error',error);
90 | let weatherString = "Failed to fetch weather data";
91 | return weatherString;
92 | }
93 | }
94 |
95 | }
96 |
97 | // ╭──────────────────────────────────────────────────────────────────────────────╮
98 | // │ ● Class OpenWeather ● │
99 | // │ │
100 | // │ • The Plugin class defines the lifecycle of a plugin • │
101 | // ╰──────────────────────────────────────────────────────────────────────────────╯
102 | export default class OpenWeather extends Plugin {
103 | settings: OpenWeatherSettings;
104 | statusBar: HTMLElement;
105 | divEl: HTMLElement;
106 | plugin: any;
107 |
108 | // • onload - Configure resources needed by the plugin •
109 | async onload() {
110 |
111 | // onload - Load settings
112 | await this.loadSettings();
113 | // weatherFormat3: '%icon% %dateMonth4% %dateDay2% %dateYear1% • %timeH2%:%timeM% %ampm1% • %desc%
Recorded Temp: %temp% • Felt like: %feels%
Wind: %wind-speed% km/h from the %wind-dir%^ with gusts up to %wind-gust% km/h^
Sunrise: %sunrise% • Sunset: %sunset%',
114 | // weatherFormat4: '%icon% %dateMonth4% %dateDay2% %dateYear1% • %timeH2%:%timeM% %ampm1% • %desc%
Current Temp: %temp% • Feels like: %feels%
Wind: %wind-speed% km/h from the %wind-dir%^ with gusts up to %wind-gust% km/h^
Sunrise: %sunrise% • Sunset: %sunset%',
115 | if (this.settings.weatherFormat3 == '%icon% %dateMonth4% %dateDay2% %dateYear1% • %timeH2%:%timeM% %ampm1% • %desc%
Recorded Temp: %temp% • Felt like: %feels%
Wind: %wind-speed% km/h from the %wind-dir%^ with gusts up to %wind-gust% km/h^
Sunrise: %sunrise% • Sunset: %sunset%') {
116 | this.settings.weatherFormat3 = `%icon% %dateMonth4% %dateDay2% %dateYear1% • %timeH2%:%timeM% %ampm1% • %desc%
117 | Recorded Temp: %temp% • Felt like: %feels%
118 | Wind: %wind-speed% km/h from the %wind-dir%^ with gusts up to %wind-gust% km/h^
119 | Sunrise: %sunrise% • Sunset: %sunset%`;
120 | await this.saveSettings();
121 | };
122 | if (this.settings.weatherFormat4 == '%icon% %dateMonth4% %dateDay2% %dateYear1% • %timeH2%:%timeM% %ampm1% • %desc%
Recorded Temp: %temp% • Felt like: %feels%
Wind: %wind-speed% km/h from the %wind-dir%^ with gusts up to %wind-gust% km/h^
Sunrise: %sunrise% • Sunset: %sunset%') {
123 | this.settings.weatherFormat4 = `%icon% %dateMonth4% %dateDay2% %dateYear1% • %timeH2%:%timeM% %ampm1% • %desc%
124 | Recorded Temp: %temp% • Felt like: %feels%
125 | Wind: %wind-speed% km/h from the %wind-dir%^ with gusts up to %wind-gust% km/h^
126 | Sunrise: %sunrise% • Sunset: %sunset%`;
127 | await this.saveSettings();
128 | };
129 | let configDir = app.vault.configDir;
130 | let dataJsonPath = "";
131 | let dataJsonFilePath = "";
132 | let adapter = app.vault.adapter;
133 | if (adapter instanceof FileSystemAdapter) {
134 | dataJsonPath = path.join(adapter.getBasePath(),configDir,'plugins','open-weather');
135 | dataJsonFilePath = path.join(adapter.getBasePath(),configDir,'plugins','open-weather','data.json');
136 | };
137 | let dataJson = await JSON.parse(fs.readFileSync(dataJsonFilePath, 'utf-8'));
138 | console.log('>>>>>>> TESTING\n');
139 | //console.log('settings:\n', this.settings);
140 | if (dataJson.weatherString1) {
141 | fs.renameSync(dataJsonFilePath, path.join(dataJsonPath, 'data.json.bkp'));
142 | delete dataJson.weatherString1
143 | delete dataJson.weatherString2
144 | delete dataJson.weatherString3
145 | delete dataJson.weatherString4
146 | delete dataJson.weatherStringSB
147 | fs.writeFileSync(dataJsonFilePath, JSON.stringify(dataJson, null, 2));
148 | await this.loadSettings();
149 | await this.saveSettings();
150 | //console.log('dataJson:\n', dataJson);
151 | };
152 | //await this.onPick.bind(this, this.plugin, this.settings);
153 | //OpenWeather.prototype.onPick.bind(this.settings.location);
154 | //await this.onload.bind(this, this.plugin, this.settings);
155 |
156 | // onload - This creates an icon in the left ribbon
157 | this.addRibbonIcon('thermometer-snowflake', 'OpenWeather', (evt: MouseEvent) => {
158 | // Called when the user clicks the icon.
159 | const view = this.app.workspace.getActiveViewOfType(MarkdownView);
160 | if (!view) {
161 | new Notice("Open a Markdown file first.");
162 | return;
163 | }
164 | new InsertWeatherPicker(this.settings.location, this.settings.latitude, this.settings.longitude, this.settings.key, this.settings.units, this.settings.language, this.settings.weatherFormat1, this.settings.weatherFormat2, this.settings.weatherFormat3, this.settings.weatherFormat4).open();
165 | });
166 |
167 | // onload - This adds a status bar item to the bottom of the app - Does not work on mobile apps
168 | this.statusBar = this.addStatusBarItem();
169 | if (this.settings.statusbarActive) {
170 | if (this.settings.key.length == 0 || this.settings.location.length == 0) {
171 | if (displayErrorMsg) {
172 | new Notice("OpenWeather plugin settings are undefined, check your settings.", 8000)
173 | this.statusBar.setText('');
174 | console.log('Err:', displayErrorMsg);
175 | displayErrorMsg = false;
176 | }
177 | } else {
178 | let wstr = new FormatWeather(this.settings.location, this.settings.latitude, this.settings.longitude, this.settings.key, this.settings.units, this.settings.language, this.settings.weatherFormatSB);
179 | let weatherStr = await wstr.getWeatherString();
180 | if (weatherStr.length == 0) {return};
181 | this.statusBar.setText(weatherStr);
182 | }
183 | } else {
184 | this.statusBar.setText('');
185 | }
186 |
187 | // onload - Replace template strings
188 | this.addCommand ({
189 | id: 'replace-template-string',
190 | name: 'Replace template strings',
191 | editorCallback: async (editor: Editor, view: MarkdownView) => {
192 | if (this.settings.weatherFormat1.length > 0) {
193 | if (view.data.contains("%weather1%")) {
194 | let wstr = new FormatWeather (this.settings.location, this.settings.latitude, this.settings.longitude, this.settings.key, this.settings.units, this.settings.language, this.settings.weatherFormat1);
195 | let weatherStr = await wstr.getWeatherString();
196 | if (weatherStr.length == 0) {return};
197 | let doc = editor.getValue().replace(/%weather1%/gmi, weatherStr);
198 | editor.setValue(doc);
199 | }
200 | }
201 | if (this.settings.weatherFormat2.length > 0) {
202 | if (view.data.contains("%weather2%")) {
203 | let wstr = new FormatWeather (this.settings.location, this.settings.latitude, this.settings.longitude, this.settings.key, this.settings.units, this.settings.language, this.settings.weatherFormat2);
204 | let weatherStr = await wstr.getWeatherString();
205 | if (weatherStr.length == 0) {return};
206 | let doc = editor.getValue().replace(/%weather2%/gmi, weatherStr);
207 | editor.setValue(doc);
208 | }
209 | }
210 | if (this.settings.weatherFormat3.length > 0) {
211 | if (view.data.contains("%weather3%")) {
212 | let wstr = new FormatWeather (this.settings.location, this.settings.latitude, this.settings.longitude, this.settings.key, this.settings.units, this.settings.language, this.settings.weatherFormat3);
213 | let weatherStr = await wstr.getWeatherString();
214 | if (weatherStr.length == 0) {return};
215 | let doc = editor.getValue().replace(/%weather3%/gmi, weatherStr);
216 | editor.setValue(doc);
217 | }
218 | }
219 | if (this.settings.weatherFormat4.length > 0) {
220 | if (view.data.contains("%weather4%")) {
221 | let wstr = new FormatWeather (this.settings.location, this.settings.latitude, this.settings.longitude, this.settings.key, this.settings.units, this.settings.language, this.settings.weatherFormat4);
222 | let weatherStr = await wstr.getWeatherString();
223 | if (weatherStr.length == 0) {return};
224 | let doc = editor.getValue().replace(/%weather4%/gmi, weatherStr);
225 | editor.setValue(doc);
226 | }
227 | }
228 | }
229 | });
230 |
231 | // onload - Insert weather string one
232 | this.addCommand ({
233 | id: 'insert-string-one',
234 | name: 'Insert weather string one',
235 | editorCallback: async (editor: Editor, view: MarkdownView) => {
236 | if (this.settings.weatherFormat1.length > 0) {
237 | let wstr = new FormatWeather (this.settings.location, this.settings.latitude, this.settings.longitude, this.settings.key, this.settings.units, this.settings.language, this.settings.weatherFormat1);
238 | let weatherStr = await wstr.getWeatherString();
239 | if (weatherStr.length == 0) {return};
240 | editor.replaceSelection(`${weatherStr}`);
241 | } else {
242 | new Notice('Weather string 1 is undefined! Please add a definition for it in the OpenWeather plugin settings.', 5000);
243 | }
244 | }
245 | });
246 |
247 | // onload - Insert weather string two
248 | this.addCommand ({
249 | id: 'insert-string-two',
250 | name: 'Insert weather string two',
251 | editorCallback: async (editor: Editor, view: MarkdownView) => {
252 | if (this.settings.weatherFormat2.length > 0) {
253 | let wstr = new FormatWeather (this.settings.location, this.settings.latitude, this.settings.longitude, this.settings.key, this.settings.units, this.settings.language, this.settings.weatherFormat2);
254 | let weatherStr = await wstr.getWeatherString();
255 | if (weatherStr.length == 0) {return};
256 | editor.replaceSelection(`${weatherStr}`);
257 | } else {
258 | new Notice('Weather string 2 is undefined! Please add a definition for it in the OpenWeather plugin settings.', 5000);
259 | }
260 | }
261 | });
262 |
263 | // onload - Insert weather string three
264 | this.addCommand ({
265 | id: 'insert-string-three',
266 | name: 'Insert weather string three',
267 | editorCallback: async (editor: Editor, view: MarkdownView) => {
268 | if (this.settings.weatherFormat3.length > 0) {
269 | let wstr = new FormatWeather (this.settings.location, this.settings.latitude, this.settings.longitude, this.settings.key, this.settings.units, this.settings.language, this.settings.weatherFormat3);
270 | let weatherStr = await wstr.getWeatherString();
271 | if (weatherStr.length == 0) {return};
272 | editor.replaceSelection(`${weatherStr}`);
273 | } else {
274 | new Notice('Weather string 3 is undefined! Please add a definition for it in the OpenWeather plugin settings.', 5000);
275 | }
276 | }
277 | });
278 |
279 | // onload - Insert weather string four
280 | this.addCommand ({
281 | id: 'insert-string-four',
282 | name: 'Insert weather string four',
283 | editorCallback: async (editor: Editor, view: MarkdownView) => {
284 | if (this.settings.weatherFormat4.length > 0) {
285 | let wstr = new FormatWeather (this.settings.location, this.settings.latitude, this.settings.longitude, this.settings.key, this.settings.units, this.settings.language, this.settings.weatherFormat4);
286 | let weatherStr = await wstr.getWeatherString();
287 | if (weatherStr.length == 0) {return};
288 | editor.replaceSelection(`${weatherStr}`);
289 | } else {
290 | new Notice('Weather string 4 is undefined! Please add a definition for it in the OpenWeather plugin settings.', 5000);
291 | }
292 | }
293 | });
294 |
295 | // onload - This adds a settings tab so the user can configure various aspects of the plugin
296 | this.addSettingTab(new OpenWeatherSettingsTab(this.app, this, this.settings));
297 |
298 | // onload - registerEvent - 'file-open'
299 | this.registerEvent(this.app.workspace.on('file-open', async (file) => {
300 | if (file) {
301 | await new Promise(r => setTimeout(r, 2000)); // Wait for Templater to do its thing
302 | await this.replaceTemplateStrings();
303 | await this.updateCurrentWeatherDiv();
304 | }
305 | }));
306 |
307 | // onload - registerEvent - 'layout-change'
308 | this.registerEvent(this.app.workspace.on('layout-change', async () => {
309 | await new Promise(r => setTimeout(r, 2000)); // Wait for Templater to do its thing
310 | await this.replaceTemplateStrings();
311 | await this.updateCurrentWeatherDiv();
312 | }));
313 |
314 | // onload - registerEvent - 'resolved'
315 | this.registerEvent(this.app.metadataCache.on('resolved', async () => {
316 | await this.replaceTemplateStrings();
317 | await this.updateCurrentWeatherDiv();
318 | }));
319 |
320 | // onload - When registering intervals, this function will automatically clear the interval when the plugin is disabled
321 | let updateFreq = this.settings.statusbarUpdateFreq;
322 | this.registerInterval(window.setInterval(() => this.updateWeather(), Number(updateFreq) * 60 * 1000));
323 | this.registerInterval(window.setInterval(() => this.updateCurrentWeatherDiv(), Number(updateFreq) * 60 * 1000));
324 |
325 | }
326 |
327 | // • updateCurrentWeatherDiv - Update DIV's with current weather •
328 | async updateCurrentWeatherDiv() {
329 | const view = this.app.workspace.getActiveViewOfType(MarkdownView);
330 | if (!view) return;
331 | if(document.getElementsByClassName('weather_current_1').length === 1) {
332 | const divEl = document.getElementsByClassName('weather_current_1')[0];
333 | let wstr = new FormatWeather (this.settings.location, this.settings.latitude, this.settings.longitude, this.settings.key, this.settings.units, this.settings.language, this.settings.weatherFormat1);
334 | let weatherStr = await wstr.getWeatherString();
335 | if (weatherStr.length == 0) {return};
336 | divEl.innerHTML = weatherStr;
337 | }
338 | if(document.getElementsByClassName('weather_current_2').length === 1) {
339 | const divEl = document.getElementsByClassName('weather_current_2')[0];
340 | let wstr = new FormatWeather (this.settings.location, this.settings.latitude, this.settings.longitude, this.settings.key, this.settings.units, this.settings.language, this.settings.weatherFormat2);
341 | let weatherStr = await wstr.getWeatherString();
342 | if (weatherStr.length == 0) {return};
343 | divEl.innerHTML = weatherStr;
344 | }
345 | if(document.getElementsByClassName('weather_current_3').length === 1) {
346 | const divEl = document.getElementsByClassName('weather_current_3')[0];
347 | let wstr = new FormatWeather (this.settings.location, this.settings.latitude, this.settings.longitude, this.settings.key, this.settings.units, this.settings.language, this.settings.weatherFormat3);
348 | let weatherStr = await wstr.getWeatherString();
349 | divEl.innerHTML = weatherStr;
350 | }
351 | if(document.getElementsByClassName('weather_current_4').length === 1) {
352 | const divEl = document.getElementsByClassName('weather_current_4')[0];
353 | let wstr = new FormatWeather (this.settings.location, this.settings.latitude, this.settings.longitude, this.settings.key, this.settings.units, this.settings.language, this.settings.weatherFormat4);
354 | let weatherStr = await wstr.getWeatherString();
355 | if (weatherStr.length == 0) {return};
356 | divEl.innerHTML = weatherStr;
357 | }
358 | }
359 |
360 | // • replaceTemplateStrings - Replace any template strings in current file •
361 | async replaceTemplateStrings() {
362 | const view = this.app.workspace.getActiveViewOfType(MarkdownView);
363 | if (!view) return;
364 | const file = app.workspace.getActiveFile();
365 | if (view.file.parent.path.includes(this.settings.excludeFolder)) return; // Ignore this folder and any subfolders for Template String Replacement
366 | let editor = view.getViewData();
367 | if (editor == null) return;
368 | if (this.settings.weatherFormat1.length > 0) {
369 | if (editor.contains("%weather1%")) {
370 | let wstr = new FormatWeather (this.settings.location, this.settings.latitude, this.settings.longitude, this.settings.key, this.settings.units, this.settings.language, this.settings.weatherFormat1);
371 | let weatherStr = await wstr.getWeatherString();
372 | if (weatherStr.length == 0) {return};
373 | editor = editor.replace(/%weather1%/gmi, weatherStr);
374 | file?.vault.modify(file, editor);
375 | }
376 | }
377 | if (this.settings.weatherFormat2.length > 0) {
378 | if (editor.contains("%weather2%")) {
379 | let wstr = new FormatWeather (this.settings.location, this.settings.latitude, this.settings.longitude, this.settings.key, this.settings.units, this.settings.language, this.settings.weatherFormat2);
380 | let weatherStr = await wstr.getWeatherString();
381 | if (weatherStr.length == 0) {return};
382 | editor = editor.replace(/%weather2%/gmi, weatherStr);
383 | file?.vault.modify(file, editor);
384 | }
385 | }
386 | if (this.settings.weatherFormat3.length > 0) {
387 | if (editor.contains("%weather3%")) {
388 | let wstr = new FormatWeather (this.settings.location, this.settings.latitude, this.settings.longitude, this.settings.key, this.settings.units, this.settings.language, this.settings.weatherFormat3);
389 | let weatherStr = await wstr.getWeatherString();
390 | if (weatherStr.length == 0) {return};
391 | editor = editor.replace(/%weather3%/gmi, weatherStr);
392 | file?.vault.modify(file, editor);
393 | }
394 | }
395 | if (this.settings.weatherFormat4.length > 0) {
396 | if (editor.contains("%weather4%")) {
397 | let wstr = new FormatWeather (this.settings.location, this.settings.latitude, this.settings.longitude, this.settings.key, this.settings.units, this.settings.language, this.settings.weatherFormat4);
398 | let weatherStr = await wstr.getWeatherString();
399 | if (weatherStr.length == 0) {return};
400 | editor = editor.replace(/%weather4%/gmi, weatherStr);
401 | file?.vault.modify(file, editor);
402 | }
403 | }
404 | }
405 |
406 | // • updateWeather - Get weather information from OpenWeather API •
407 | async updateWeather() {
408 | if (this.settings.statusbarActive) {
409 | if (this.settings.key.length == 0 || this.settings.location.length == 0) {
410 | if (displayErrorMsg) {
411 | new Notice("OpenWeather plugin settings are undefined, check your settings.");
412 | this.statusBar.setText('');
413 | displayErrorMsg = false;
414 | }
415 | } else {
416 | let wstr = new FormatWeather(this.settings.location, this.settings.latitude, this.settings.longitude, this.settings.key, this.settings.units, this.settings.language, this.settings.weatherFormatSB);
417 | let weatherStr = await wstr.getWeatherString();
418 | if (weatherStr.length == 0) {return};
419 | this.statusBar.setText(weatherStr);
420 | }
421 | } else {
422 | this.statusBar.setText('');
423 | }
424 | }
425 |
426 | // • onunload - Release any resources configured by the plugin •
427 | onunload() {
428 |
429 | }
430 |
431 | // • loadSettings - Load settings from data.json •
432 | async loadSettings() {
433 | this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
434 | }
435 |
436 | // • saveSettings - Save settings from data.json •
437 | async saveSettings() {
438 | await this.saveData(this.settings);
439 | }
440 |
441 | // • onPick - Callback for pick city suggester •
442 | async onPick(picked: any, settings:any) {
443 | // this.settings = settings;
444 | // console.log('Finally got here!!!!!!', this.settings.location);
445 |
446 | let name = picked.name;
447 | let lat = picked.lat;
448 | let lon = picked.lon;
449 | // this.settings.location = name;
450 | // this.settings.latitude = lat;
451 | // this.settings.longitude = lon;
452 |
453 | // Input event for update to input boxes location, lattitude and longitude
454 | const inputEvent = new InputEvent('input', {
455 | bubbles: true,
456 | cancelable: true,
457 | composed: true,
458 | inputType: 'insertText',
459 | data: 'your_data'
460 | });
461 |
462 | // Update location, latitude and longitude with the data from selected city
463 | // HACK: Have not been able to get `this` to work from this method so forced to
464 | // update them directly. Need to find a way to fix this - .bind() did not work for me?
465 | let inputEls = document.getElementsByTagName("input");
466 | for (let idx = 0; idx < inputEls.length; idx++) {
467 | if (inputEls[idx].placeholder == "Enter city Eg. Chicago or South Bend,WA,US") {
468 | inputEls[idx].value = name;
469 | inputEls[idx].dispatchEvent(inputEvent);
470 | };
471 | if (inputEls[idx].placeholder == "53.5501") {
472 | inputEls[idx].value = lat;
473 | inputEls[idx].dispatchEvent(inputEvent);
474 | };
475 | if (inputEls[idx].placeholder == "-113.4687") {
476 | inputEls[idx].value = lon;
477 | inputEls[idx].dispatchEvent(inputEvent);
478 | };
479 | };
480 | //await this.plugin.saveSettings();
481 | //await this.plugin.updateWeather();
482 | //OpenWeather.prototype.saveSettings();
483 | //OpenWeather.prototype.updateWeather();
484 | //this.saveSettings();
485 | //this.saveSettings();
486 | //this.updateWeather();
487 | }
488 | }
489 |
490 | // ╭──────────────────────────────────────────────────────────────────────────────╮
491 | // │ ● Class InsertWeatherModal ● │
492 | // │ │
493 | // │ • Insert Weather or Replace Template Strings Modal • │
494 | // ╰──────────────────────────────────────────────────────────────────────────────╯
495 | interface Commands {
496 | command: string;
497 | format: string;
498 | }
499 |
500 | let ALL_COMMANDS: { command: string; format: string; }[] = [];
501 |
502 | class InsertWeatherPicker extends SuggestModal implements OpenWeatherSettings{
503 | location: string;
504 | latitude: string;
505 | longitude: string;
506 | key: string;
507 | units: string;
508 | language: string;
509 | excludeFolder: string;
510 | weatherFormat1: string;
511 | weatherFormat2: string;
512 | weatherFormat3: string;
513 | weatherFormat4: string;
514 | statusbarActive: boolean;
515 | weatherFormatSB: string;
516 | statusbarUpdateFreq: string;
517 | plugin: OpenWeather
518 | command: string;
519 | format: string;
520 |
521 | constructor(location: string, latitude: string, longitude: string, key: string, units: string, language: string, weatherFormat1: string, weatherFormat2: string, weatherFormat3: string, weatherFormat4: string) {
522 | super(app);
523 | this.location = location;
524 | this.latitude = latitude;
525 | this.longitude = longitude;
526 | this.key = key;
527 | this.units = units;
528 | this.language = language;
529 | this.weatherFormat1 = weatherFormat1;
530 | this.weatherFormat2 = weatherFormat2;
531 | this.weatherFormat3 = weatherFormat3;
532 | this.weatherFormat4 = weatherFormat4;
533 | }
534 |
535 | async getSuggestions(query: string): Promise {
536 | ALL_COMMANDS = [];
537 | const view = this.app.workspace.getActiveViewOfType(MarkdownView);
538 | if (view?.getViewType() === 'markdown') {
539 | const md = view as MarkdownView;
540 | if (md.getMode() === 'source') {
541 | if (this.weatherFormat1.length > 0) {
542 | let wstr = new FormatWeather (this.location, this.latitude, this.longitude, this.key, this.units, this.language, this.weatherFormat1);
543 | let weatherStr = await wstr.getWeatherString();
544 | if (weatherStr.length > 0) {
545 | this.weatherFormat1 = weatherStr;
546 | ALL_COMMANDS.push({command: "Insert Weather String - 1", format: this.weatherFormat1})
547 | } else {
548 | this.weatherFormat1 = "";
549 | return ALL_COMMANDS.filter((command) =>
550 | command.command.toLowerCase().includes(query.toLowerCase())
551 | );
552 | }
553 | }
554 | if (this.weatherFormat2.length > 0) {
555 | let wstr = new FormatWeather (this.location, this.latitude, this.longitude, this.key, this.units, this.language, this.weatherFormat2);
556 | let weatherStr = await wstr.getWeatherString();
557 | if (weatherStr.length > 0) {
558 | this.weatherFormat2 = weatherStr;
559 | ALL_COMMANDS.push({command: "Insert Weather String - 2", format: this.weatherFormat2})
560 | } else {
561 | this.weatherFormat2 = "";
562 | return ALL_COMMANDS.filter((command) =>
563 | command.command.toLowerCase().includes(query.toLowerCase())
564 | );
565 | }
566 | }
567 | if (this.weatherFormat3.length > 0) {
568 | let wstr = new FormatWeather (this.location, this.latitude, this.longitude, this.key, this.units, this.language, this.weatherFormat3);
569 | let weatherStr = await wstr.getWeatherString();
570 | if (weatherStr.length > 0) {
571 | this.weatherFormat3 = weatherStr;
572 | ALL_COMMANDS.push({command: "Insert Weather String - 3", format: this.weatherFormat3})
573 | } else {
574 | this.weatherFormat3 = "";
575 | return ALL_COMMANDS.filter((command) =>
576 | command.command.toLowerCase().includes(query.toLowerCase())
577 | );
578 | }
579 | }
580 | if (this.weatherFormat4.length > 0) {
581 | let wstr = new FormatWeather (this.location, this.latitude, this.longitude, this.key, this.units, this.language, this.weatherFormat4);
582 | let weatherStr = await wstr.getWeatherString();
583 | if (weatherStr.length > 0) {
584 | this.weatherFormat4 = weatherStr;
585 | ALL_COMMANDS.push({command: "Insert Weather String - 4", format: this.weatherFormat4})
586 | } else {
587 | this.weatherFormat4 = "";
588 | return ALL_COMMANDS.filter((command) =>
589 | command.command.toLowerCase().includes(query.toLowerCase())
590 | );
591 | }
592 | }
593 | ALL_COMMANDS.push({command: "Replace Template Strings", format: "Replace all occurences of %weather1%, %weather2%, %weather3% and %weather4%\nin the current document."})
594 | } else {
595 | new Notice("Please enter edit mode to insert weather strings.");
596 | }
597 | }
598 | return ALL_COMMANDS.filter((command) =>
599 | command.command.toLowerCase().includes(query.toLowerCase())
600 | );
601 | }
602 |
603 | // Renders each suggestion item.
604 | renderSuggestion(command: Commands, el: HTMLElement) {
605 | el.createEl("div", { text: command.command });
606 | el.createEl("small", { text: command.format });
607 | }
608 |
609 | // Perform action on the selected suggestion.
610 | async onChooseSuggestion(command: Commands, evt: MouseEvent | KeyboardEvent) {
611 | this.command = command.command
612 | this.format = command.format
613 | this.close();
614 | const view = this.app.workspace.getActiveViewOfType(MarkdownView);
615 | if (!view) return;
616 | const file = app.workspace.getActiveFile();
617 | let editor = view.getViewData();
618 | if (editor == null) return;
619 | if (command.command == 'Replace Template Strings') {
620 | if (this.weatherFormat1.length > 0) {
621 | editor = editor.replace(/%weather1%/gmi, this.weatherFormat1);
622 | view.setViewData(editor,false);
623 | file?.vault.modify(file, editor);
624 | } else {
625 | return;
626 | }
627 | if (this.weatherFormat2.length > 0) {
628 | editor = editor.replace(/%weather2%/gmi, this.weatherFormat2);
629 | file?.vault.modify(file, editor);
630 | } else {
631 | return;
632 | }
633 | if (this.weatherFormat3.length > 0) {
634 | editor = editor.replace(/%weather3%/gmi, this.weatherFormat3);
635 | file?.vault.modify(file, editor);
636 | } else {
637 | return;
638 | }
639 | if (this.weatherFormat4.length > 0) {
640 | editor = editor.replace(/%weather4%/gmi, this.weatherFormat4);
641 | file?.vault.modify(file, editor);
642 | } else {
643 | return;
644 | }
645 | } else {
646 | view.editor.replaceSelection(this.format);
647 | }
648 | }
649 | }
650 |
651 | // ╭──────────────────────────────────────────────────────────────────────────────╮
652 | // │ ● Class CitySearchResultPicker ● │
653 | // │ │
654 | // │ • Displays City Search Results in a Suggest Modal • │
655 | // ╰──────────────────────────────────────────────────────────────────────────────╯
656 | interface CommandsCity {
657 | city: string;
658 | coords: string;
659 | index: number;
660 | selection: object;
661 | }
662 |
663 | let ALL_CITY_COMMANDS: { city: string; coords: string; index: number; selection: object }[] = [];
664 |
665 | class CitySearchResultPicker extends SuggestModal implements CitySearchResultPicker {
666 | private json: object;
667 | private settings: any;
668 |
669 | constructor(json: object, settings:any) {
670 | super(app);
671 | this.json = json;
672 | this.settings = settings;
673 | }
674 |
675 | async getSuggestions(query: string): Promise {
676 | ALL_CITY_COMMANDS = [];
677 | // {
678 | // "name": "Hell",
679 | // "lat": 42.4347571,
680 | // "lon": -83.9849477,
681 | // "country": "US",
682 | // "state": "Michigan"
683 | // },
684 |
685 | // Setup choices based on the list of cities returned from the Geocoding API
686 | let cityObjCount = Object.keys(this.json).length;
687 | let values = Object.values(this.json);
688 | for (let i = 0; i < cityObjCount; i++) {
689 | let city = values[i].name;
690 | let state = values[i].state;
691 | if (state == undefined) {state = ""};
692 | let country = values[i].country;
693 | if (country == undefined) {country = ""};
694 | let locationStr = city+" "+state+" "+country;
695 | let coords = "Latitude: "+values[i].lat+" "+"Longitude: "+values[i].lon;
696 | ALL_CITY_COMMANDS.push({city: locationStr, coords: coords, index: i, selection: values[i]});
697 | }
698 | let retry = "None of these match? Try again...";
699 | let redo = "Select to perform another search";
700 | ALL_CITY_COMMANDS.push({city: retry, coords: redo, index: 9999, selection: {}});
701 | return ALL_CITY_COMMANDS.filter((command) =>
702 | command.city.toLowerCase().includes(query.toLowerCase())
703 | );
704 | }
705 |
706 | // Renders each suggestion item.
707 | renderSuggestion(command: CommandsCity, el: HTMLElement) {
708 | el.createEl("div", { text: command.city });
709 | el.createEl("small", { text: command.coords });
710 | }
711 |
712 | // Perform action on the selected suggestion.
713 | onChooseSuggestion(command: CommandsCity, evt: MouseEvent | KeyboardEvent) {
714 | if (command.index == 9999) {return}; // User selected retry
715 | OpenWeather.prototype.onPick(command.selection,this.settings);
716 | }
717 | };
718 |
719 |
720 | // ╭──────────────────────────────────────────────────────────────────────────────╮
721 | // │ ● Class OpenWeatherSettingsTab ● │
722 | // │ │
723 | // │ • Open Weather Settings Tab • │
724 | // ╰──────────────────────────────────────────────────────────────────────────────╯
725 | class OpenWeatherSettingsTab extends PluginSettingTab {
726 | plugin: OpenWeather;
727 | citySearch: string;
728 | cityObj: object;
729 | jsonCities: object;
730 | settings:any
731 |
732 | constructor(app: App, plugin: OpenWeather, settings:any) {
733 | super(app, plugin);
734 | this.plugin = plugin;
735 | this.settings = settings;
736 | }
737 |
738 | display(): void {
739 | const {containerEl} = this;
740 | containerEl.empty();
741 | let citySearch: string = this.plugin.settings.location;
742 |
743 | // Get vault folders for exclude folder dropdown list
744 | const abstractFiles = app.vault.getAllLoadedFiles();
745 | const folders: TFolder[] = [];
746 | abstractFiles.forEach((folder: TAbstractFile) => {
747 | if (
748 | folder instanceof TFolder && folder.name.length > 0
749 | ) {
750 | folders.push(folder);
751 | }
752 | });
753 |
754 | new Setting(containerEl)
755 | .setName('View on Github')
756 | .addButton(cb => {
757 | cb.setButtonText('GitHub Readme.md');
758 | cb.setTooltip('Visit OpenWeather Readme.md for detailed plugin information');
759 | cb.onClick(() => {
760 | window.open(`https://github.com/willasm/obsidian-open-weather/#openweather-plugin-for-obsidian`, '_blank');
761 | });
762 | })
763 | .addButton(cb => {
764 | cb.setButtonText('Example.md');
765 | cb.setTooltip('A detailed explanation of how I use the plugin in my daily note');
766 | cb.onClick(() => {
767 | window.open(`https://github.com/willasm/obsidian-open-weather/blob/master/EXAMPLE.md`, '_blank');
768 | });
769 | })
770 | .addButton(cb => {
771 | cb.setButtonText('Report Issue');
772 | cb.setTooltip('Have any questions or comments? Feel free to post them here');
773 | cb.onClick(() => {
774 | window.open(`https://github.com/willasm/obsidian-open-weather/issues`, '_blank');
775 | });
776 | })
777 |
778 | // OpenWeatherSettingsTab - H2 Header - OpenWeather API Authentication key (required)
779 | containerEl.createEl('h2', {text: 'OpenWeather API Authentication key (required)'});
780 |
781 | new Setting(containerEl)
782 | .setName('OpenWeather API key')
783 | .setDesc('A free OpenWeather API key is required for the plugin to work. Go to https://openweathermap.org to register and get a key')
784 | .addText(text => text
785 | .setPlaceholder('Enter OpenWeather API key')
786 | .setValue(this.plugin.settings.key)
787 | .onChange(async (value) => {
788 | this.plugin.settings.key = value;
789 | await this.plugin.saveSettings();
790 | await this.plugin.updateWeather();
791 | }));
792 |
793 | // OpenWeatherSettingsTab - H2 Header - Location
794 | containerEl.createEl('h2', {text: 'Location (required)'});
795 |
796 | new Setting(containerEl)
797 | .setName('Use Geocoding API to get location (recommended)')
798 | .setDesc('The Geocoding API returns the requested locations name, state, country, latitude and longitude')
799 | .addText(text => text
800 | .setPlaceholder('City name to search for')
801 | //.setValue(this.plugin.settings.location)
802 | .onChange(async (value) => {
803 | citySearch = value;
804 | }))
805 | .addButton(cb => {
806 | cb.setButtonText('Get location');
807 | //cb.setCta();
808 | cb.onClick(async () => {
809 | let key = this.plugin.settings.key;
810 | //let city = citySearch;
811 | if (key.length == 0) {
812 | new Notice("OpenWeather API key is required");
813 | } else {
814 | let url;
815 | url = `https://api.openweathermap.org/geo/1.0/direct?q=${citySearch}&limit=5&appid=${key}`;
816 | let req = await fetch(url);
817 | let jsonCitiesObj = await req.json();
818 | new CitySearchResultPicker(jsonCitiesObj,this.settings).open();
819 | }
820 | })
821 | });
822 |
823 | new Setting(containerEl)
824 | .setName('Enter location')
825 | .setDesc('Name of the city you want to retrieve weather for. Also supports {city name},{country code} or {city name},{state code},{country code} Eg. South Bend,WA,US Please note that searching by states available only for the USA locations')
826 | .addText(text => text
827 | .setPlaceholder('Enter city Eg. Chicago or South Bend,WA,US')
828 | .setValue(this.plugin.settings.location)
829 | .onChange(async (value) => {
830 | this.plugin.settings.location = value;
831 | await this.plugin.saveSettings();
832 | await this.plugin.updateWeather();
833 | }));
834 |
835 | new Setting(containerEl)
836 | .setName('Enter latitude')
837 | .setDesc('Please note that API requests by city name have been deprecated although they are still available for use. The preferred method is to use latitude and longitude.')
838 | .addText(text => text
839 | .setPlaceholder('53.5501')
840 | .setValue(this.plugin.settings.latitude)
841 | .onChange(async (value) => {
842 | this.plugin.settings.latitude = value;
843 | await this.plugin.saveSettings();
844 | await this.plugin.updateWeather();
845 | }));
846 |
847 | new Setting(containerEl)
848 | .setName('Enter longitude')
849 | .setDesc('Please note that API requests by city name have been deprecated although they are still available for use. The preferred method is to use latitude and longitude.')
850 | .addText(text => text
851 | .setPlaceholder('-113.4687')
852 | .setValue(this.plugin.settings.longitude)
853 | .onChange(async (value) => {
854 | this.plugin.settings.longitude = value;
855 | await this.plugin.saveSettings();
856 | await this.plugin.updateWeather();
857 | }));
858 |
859 | // OpenWeatherSettingsTab - H2 Header - General preferences
860 | containerEl.createEl('h2', {text: 'General preferences'});
861 |
862 | new Setting(containerEl)
863 | .setName("Units of measurement")
864 | .setDesc("Units of measurement. Metric, Imperial and Standard units are available")
865 | .addDropdown(dropDown => {
866 | dropDown.addOption('metric', 'Metric');
867 | dropDown.addOption('imperial', 'Imperial');
868 | dropDown.addOption('standard', 'Standard');
869 | dropDown.onChange(async (value) => {
870 | this.plugin.settings.units = value;
871 | await this.plugin.saveSettings();
872 | await this.plugin.updateWeather();
873 | })
874 | .setValue(this.plugin.settings.units);
875 | });
876 |
877 | new Setting(containerEl)
878 | .setName("Language")
879 | .setDesc("Supported languages available Note: This only affects text returned from the OpenWeather API")
880 | .addDropdown(dropDown => {
881 | dropDown.addOption('af', 'Afrikaans');
882 | dropDown.addOption('al', 'Albanian');
883 | dropDown.addOption('ar', 'Arabic');
884 | dropDown.addOption('az', 'Azerbaijani');
885 | dropDown.addOption('bg', 'Bulgarian');
886 | dropDown.addOption('ca', 'Catalan');
887 | dropDown.addOption('cz', 'Czech');
888 | dropDown.addOption('da', 'Danish');
889 | dropDown.addOption('de', 'German');
890 | dropDown.addOption('el', 'Greek');
891 | dropDown.addOption('en', 'English');
892 | dropDown.addOption('eu', 'Basque');
893 | dropDown.addOption('fa', 'Persian (Farsi)');
894 | dropDown.addOption('fi', 'Finnish');
895 | dropDown.addOption('fr', 'French');
896 | dropDown.addOption('gl', 'Galician');
897 | dropDown.addOption('he', 'Hebrew');
898 | dropDown.addOption('hi', 'Hindi');
899 | dropDown.addOption('hr', 'Croatian');
900 | dropDown.addOption('hu', 'Hungarian');
901 | dropDown.addOption('id', 'Indonesian');
902 | dropDown.addOption('it', 'Italian');
903 | dropDown.addOption('ja', 'Japanese');
904 | dropDown.addOption('kr', 'Korean');
905 | dropDown.addOption('la', 'Latvian');
906 | dropDown.addOption('lt', 'Lithuanian');
907 | dropDown.addOption('mk', 'Macedonian');
908 | dropDown.addOption('no', 'Norwegian');
909 | dropDown.addOption('nl', 'Dutch');
910 | dropDown.addOption('pl', 'Polish');
911 | dropDown.addOption('pt', 'Portuguese');
912 | dropDown.addOption('pt_br', 'Português Brasil');
913 | dropDown.addOption('ro', 'Romanian');
914 | dropDown.addOption('ru', 'Russian');
915 | dropDown.addOption('sv', 'Swedish');
916 | dropDown.addOption('sk', 'Slovak');
917 | dropDown.addOption('sl', 'Slovenian');
918 | dropDown.addOption('sp', 'Spanish');
919 | dropDown.addOption('sr', 'Serbian');
920 | dropDown.addOption('th', 'Thai');
921 | dropDown.addOption('tr', 'Turkish');
922 | dropDown.addOption('ua', 'Ukrainian');
923 | dropDown.addOption('vi', 'Vietnamese');
924 | dropDown.addOption('zh_cn', 'Chinese Simplified');
925 | dropDown.addOption('zh_tw', 'Chinese Traditional');
926 | dropDown.addOption('zu', 'Zulu');
927 | dropDown.onChange(async (value) => {
928 | this.plugin.settings.language = value;
929 | await this.plugin.saveSettings();
930 | await this.plugin.updateWeather();
931 | })
932 | .setValue(this.plugin.settings.language);
933 | });
934 |
935 | // OpenWeatherSettingsTab - H2 Header - Exclude Folder (Exclude Folder for Template String Replacement)
936 | containerEl.createEl('h2', {text: 'Folder to exclude from automatic template strings replacement'});
937 |
938 | new Setting(containerEl)
939 | .setName("Exclude folder")
940 | .setDesc("Folder to exclude from automatic template string replacement")
941 | .addDropdown(dropDown => {
942 | folders.forEach(e => {
943 | dropDown.addOption(e.name,e.name);
944 | });
945 | dropDown.onChange(async (value) => {
946 | this.plugin.settings.excludeFolder = value;
947 | await this.plugin.saveSettings();
948 | })
949 | .setValue(this.plugin.settings.excludeFolder);
950 | });
951 |
952 | // OpenWeatherSettingsTab - H2 Header - Weather Strings Formatting (4 Strings are Available)
953 | containerEl.createEl('h2', {text: 'Weather strings formatting (4 strings are available)'});
954 |
955 | new Setting(containerEl)
956 | .setName("Weather string 1")
957 | .setDesc("Feel free to change this to whatever you like. See the README.md on Github for all available macros.")
958 | .addTextArea((textArea: TextAreaComponent) => {
959 | textArea
960 | .setPlaceholder('Weather string 1')
961 | .setValue(this.plugin.settings.weatherFormat1)
962 | .onChange(async (value) => {
963 | this.plugin.settings.weatherFormat1 = value;
964 | await this.plugin.saveSettings();
965 | })
966 | textArea.inputEl.setAttr("rows", 10);
967 | textArea.inputEl.setAttr("cols", 60);
968 | });
969 |
970 | new Setting(containerEl)
971 | .setName("Weather string 2")
972 | .setDesc("Feel free to change this to whatever you like. See the README.md on Github for all available macros.")
973 | .addTextArea((textArea: TextAreaComponent) => {
974 | textArea
975 | .setPlaceholder('Weather string 2')
976 | .setValue(this.plugin.settings.weatherFormat2)
977 | .onChange(async (value) => {
978 | this.plugin.settings.weatherFormat2 = value;
979 | await this.plugin.saveSettings();
980 | })
981 | textArea.inputEl.setAttr("rows", 10);
982 | textArea.inputEl.setAttr("cols", 60);
983 | });
984 |
985 | new Setting(containerEl)
986 | .setName("Weather string 3")
987 | .setDesc("Feel free to change this to whatever you like. See the README.md on Github for all available macros.")
988 | .addTextArea((textArea: TextAreaComponent) => {
989 | textArea
990 | .setPlaceholder('Weather string 3')
991 | .setValue(this.plugin.settings.weatherFormat3)
992 | .onChange(async (value) => {
993 | this.plugin.settings.weatherFormat3 = value;
994 | await this.plugin.saveSettings();
995 | })
996 | textArea.inputEl.setAttr("rows", 10);
997 | textArea.inputEl.setAttr("cols", 60);
998 | });
999 |
1000 | new Setting(containerEl)
1001 | .setName("Weather string 4")
1002 | .setDesc("Feel free to change this to whatever you like. See the README.md on Github for all available macros.")
1003 | .addTextArea((textArea: TextAreaComponent) => {
1004 | textArea
1005 | .setPlaceholder('Weather string 4')
1006 | .setValue(this.plugin.settings.weatherFormat4)
1007 | .onChange(async (value) => {
1008 | this.plugin.settings.weatherFormat4 = value;
1009 | await this.plugin.saveSettings();
1010 | })
1011 | textArea.inputEl.setAttr("rows", 10);
1012 | textArea.inputEl.setAttr("cols", 60);
1013 | });
1014 |
1015 | // OpenWeatherSettingsTab - H2 Header - Show Weather in Statusbar Options
1016 | if (Platform.isDesktop) {
1017 | containerEl.createEl('h2', {text: 'Show weather in statusbar options'});
1018 |
1019 | new Setting(containerEl)
1020 | .setName('Show weather in statusbar')
1021 | .setDesc('Enable weather display in statusbar')
1022 | .addToggle(toggle => toggle
1023 | .setValue(this.plugin.settings.statusbarActive)
1024 | .onChange(async (value) => {
1025 | this.plugin.settings.statusbarActive = value;
1026 | await this.plugin.saveSettings();
1027 | await this.plugin.updateWeather();
1028 | }));
1029 |
1030 | new Setting(containerEl)
1031 | .setName("Weather string format statusbar")
1032 | .setDesc("Weather string format for the statusbar")
1033 | .addTextArea((textArea: TextAreaComponent) => {
1034 | textArea
1035 | .setPlaceholder('Statusbar Weather Format')
1036 | .setValue(this.plugin.settings.weatherFormatSB)
1037 | .onChange(async (value) => {
1038 | this.plugin.settings.weatherFormatSB = value;
1039 | await this.plugin.saveSettings();
1040 | await this.plugin.updateWeather();
1041 | })
1042 | textArea.inputEl.setAttr("rows", 10);
1043 | textArea.inputEl.setAttr("cols", 60);
1044 | });
1045 | } else {
1046 | this.plugin.settings.statusbarActive = false; // Set statusbar inactive for mobile
1047 | }
1048 |
1049 | // OpenWeatherSettingsTab - H2 Header - Show Weather in Statusbar and Dynamic DIV's update frequency
1050 | containerEl.createEl('h2', {text: `Weather update frequency`});
1051 |
1052 | new Setting(containerEl)
1053 | .setName("Update frequency")
1054 | .setDesc("Update frequency for weather information displayed on the statusbar and in dynamic DIV's")
1055 | .addDropdown(dropDown => {
1056 | dropDown.addOption('1', 'Every Minute');
1057 | dropDown.addOption('5', 'Every 5 Minutes');
1058 | dropDown.addOption('10', 'Every 10 Minutes');
1059 | dropDown.addOption('15', 'Every 15 Minutes');
1060 | dropDown.addOption('20', 'Every 20 Minutes');
1061 | dropDown.addOption('30', 'Every 30 Minutes');
1062 | dropDown.addOption('60', 'Every Hour');
1063 | dropDown.onChange(async (value) => {
1064 | this.plugin.settings.statusbarUpdateFreq = value;
1065 | await this.plugin.saveSettings();
1066 | await this.plugin.updateWeather();
1067 | })
1068 | .setValue(this.plugin.settings.statusbarUpdateFreq)
1069 | });
1070 | }
1071 | };
1072 |
1073 | // Weather Placeholders
1074 | // ====================
1075 | // weather.description %desc%
1076 | // weather.icon %icon%
1077 | // main.temp %temp%
1078 | // main.feels_like %feels%
1079 | // main.temp_min %tempmin%
1080 | // main.temp_max %tempmax%
1081 | // main.pressure %pressure%
1082 | // main.humidity %humidity%
1083 | // main.sea_level %pressure-sl%
1084 | // main.grnd_level %pressure-gl%
1085 | // visibility %visibility%
1086 | // wind.speed %wind-speed%
1087 | // wind.speedms %wind-speed-ms%
1088 | // wind.deg %wind-deg%
1089 | // wind.gust %wind-gust%
1090 | // dt (date time) %date% %time%
1091 | // sys.sunrise %sunrise%
1092 | // sys.sunset %sunset%
1093 |
1094 | // Example of API response
1095 | // =======================
1096 | //
1097 | // {
1098 | // "coord": {
1099 | // "lon": 10.99,
1100 | // "lat": 44.34
1101 | // },
1102 | // "weather": [
1103 | // {
1104 | // "id": 501,
1105 | // "main": "Rain",
1106 | // "description": "moderate rain",
1107 | // "icon": "10d"
1108 | // }
1109 | // ],
1110 | // "base": "stations",
1111 | // "main": {
1112 | // "temp": 298.48,
1113 | // "feels_like": 298.74,
1114 | // "temp_min": 297.56,
1115 | // "temp_max": 300.05,
1116 | // "pressure": 1015,
1117 | // "humidity": 64,
1118 | // "sea_level": 1015,
1119 | // "grnd_level": 933
1120 | // },
1121 | // "visibility": 10000,
1122 | // "wind": {
1123 | // "speed": 0.62,
1124 | // "deg": 349,
1125 | // "gust": 1.18
1126 | // },
1127 | // "rain": {
1128 | // "1h": 3.16
1129 | // },
1130 | // "clouds": {
1131 | // "all": 100
1132 | // },
1133 | // "dt": 1661870592,
1134 | // "sys": {
1135 | // "type": 2,
1136 | // "id": 2075663,
1137 | // "country": "IT",
1138 | // "sunrise": 1661834187,
1139 | // "sunset": 1661882248
1140 | // },
1141 | // "timezone": 7200,
1142 | // "id": 3163858,
1143 | // "name": "Zocca",
1144 | // "cod": 200
1145 | // }
1146 |
--------------------------------------------------------------------------------
/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "open-weather",
3 | "name": "OpenWeather",
4 | "version": "1.8.1",
5 | "minAppVersion": "1.4.10",
6 | "description": "This plugin returns the current weather from OpenWeather in a configurable string format.",
7 | "author": "willasm",
8 | "authorUrl": "https://github.com/willasm",
9 | "isDesktopOnly": false
10 | }
11 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "obsidian-open-weather",
3 | "version": "1.8.1",
4 | "description": "Returns weather information from OpenWeather API",
5 | "main": "main.js",
6 | "scripts": {
7 | "dev": "node esbuild.config.mjs",
8 | "build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
9 | "version": "node version-bump.mjs && git add manifest.json versions.json"
10 | },
11 | "keywords": [
12 | "weather",
13 | "plugin"
14 | ],
15 | "author": "willasm",
16 | "license": "MIT",
17 | "devDependencies": {
18 | "@types/node": "^16.11.6",
19 | "@typescript-eslint/eslint-plugin": "5.29.0",
20 | "@typescript-eslint/parser": "5.29.0",
21 | "builtin-modules": "3.3.0",
22 | "esbuild": "0.14.47",
23 | "obsidian": "latest",
24 | "tslib": "2.4.0",
25 | "typescript": "4.7.4"
26 | },
27 | "dependencies": {
28 | "date-fns": "^3.3.1"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/styles.css:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | This CSS file will be included with your plugin, and
4 | available in the app when your plugin is enabled.
5 |
6 | If your plugin does not need CSS, delete this file.
7 |
8 | */
9 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "inlineSourceMap": true,
5 | "inlineSources": true,
6 | "module": "ESNext",
7 | "target": "ES6",
8 | "allowJs": true,
9 | "noImplicitAny": true,
10 | "moduleResolution": "node",
11 | "importHelpers": true,
12 | "isolatedModules": true,
13 | "strictNullChecks": true,
14 | "lib": [
15 | "DOM",
16 | "ES5",
17 | "ES6",
18 | "ES7"
19 | ]
20 | },
21 | "include": [
22 | "**/*.ts"
23 | ]
24 | }
25 |
--------------------------------------------------------------------------------
/version-bump.mjs:
--------------------------------------------------------------------------------
1 | import { readFileSync, writeFileSync } from "fs";
2 |
3 | const targetVersion = process.env.npm_package_version;
4 |
5 | // read minAppVersion from manifest.json and bump version to target version
6 | let manifest = JSON.parse(readFileSync("manifest.json", "utf8"));
7 | const { minAppVersion } = manifest;
8 | manifest.version = targetVersion;
9 | writeFileSync("manifest.json", JSON.stringify(manifest, null, "\t"));
10 |
11 | // update versions.json with target version and minAppVersion from manifest.json
12 | let versions = JSON.parse(readFileSync("versions.json", "utf8"));
13 | versions[targetVersion] = minAppVersion;
14 | writeFileSync("versions.json", JSON.stringify(versions, null, "\t"));
15 |
--------------------------------------------------------------------------------
/versions.json:
--------------------------------------------------------------------------------
1 | {
2 | "1.0.0": "0.15.0",
3 | "1.1.0": "0.15.0",
4 | "1.2.0": "0.15.0",
5 | "1.3.0": "0.15.0",
6 | "1.4.0": "0.15.0",
7 | "1.5.0": "0.15.0",
8 | "1.5.1": "0.15.0",
9 | "1.6.0": "0.15.0",
10 | "1.6.1": "1.4.10",
11 | "1.7.0": "1.4.10",
12 | "1.7.1": "1.4.10",
13 | "1.8.0": "1.4.10",
14 | "1.8.1": "1.4.10"
15 | }
16 |
--------------------------------------------------------------------------------