1
1
namespace Loupedeck . WeatherPlugin . Commands
2
2
{
3
3
using System ;
4
+ using System . Collections . Concurrent ;
4
5
using System . Collections . Generic ;
6
+ using System . Data ;
7
+ using System . IO ;
5
8
using System . Linq ;
6
9
using System . Text ;
7
10
using System . Threading ;
8
11
using System . Timers ;
9
12
10
13
using Timer = System . Timers . Timer ;
11
14
12
- public class WeatherCommand : PluginDynamicCommand
15
+ public sealed class WeatherCommand : PluginDynamicCommand
13
16
{
14
- private readonly Services . WeatherApiService weatherApiService = new Services . WeatherApiService ( ) ;
17
+ private CancellationToken _instanceCancellation = new CancellationToken ( ) ;
18
+
19
+ private readonly Services . WeatherApiService _weatherApiService ;
15
20
private readonly static WeatherPlugin Parent = WeatherPlugin . Instance ;
16
- private System . Timers . Timer Timer = new Timer ( TimeSpan . FromMinutes ( 5 ) . TotalMilliseconds ) { Enabled = false , AutoReset = true } ;
21
+ private readonly Timer Timer = new Timer ( TimeSpan . FromMinutes ( 15 ) . TotalMilliseconds ) { Enabled = false , AutoReset = true } ;
22
+
23
+ static internal ConcurrentDictionary < string , Data > DataCache = new ConcurrentDictionary < string , Data > ( ) ;
17
24
18
- internal IDictionary < string , Data > DataCache = new Dictionary < string , Data > ( ) ;
19
25
20
26
internal class Data
21
27
{
@@ -30,9 +36,11 @@ internal class Data
30
36
31
37
public WeatherCommand ( ) : base ( "Locations" , "Weather Locations" , "Weather Locations" )
32
38
{
33
- this . MakeProfileAction ( "text;Use Zip, City Name, Postal Code, or even IP address to help get location. The input must be 'Location :API Key[:HideName]' Example: Paris :6dc3123b6e844f9e9580b205552a8c" ) ;
39
+ this . MakeProfileAction ( "text;Use zip then 2-letter ISO Country Code. \n \t The input must look like 'Zip,ISOCountryCode :API Key[:HideName]'\n \t Example: 14220,US :6dc3123b6e844f9e9580b205552a8c" ) ;
34
40
this . Timer . Elapsed += this . OnTimerElapse ;
35
41
this . Timer . Enabled = true ;
42
+
43
+ this . _weatherApiService = new Services . WeatherApiService ( ) ;
36
44
}
37
45
38
46
protected override Boolean OnLoad ( )
@@ -42,29 +50,38 @@ protected override Boolean OnLoad()
42
50
43
51
private void OnTimerElapse ( object sender , ElapsedEventArgs e )
44
52
{
45
- foreach ( var ap in this . DataCache . Keys )
53
+ foreach ( var ap in DataCache . Keys )
46
54
{
47
- this . RetrieveData ( ap ) ;
55
+ this . RetrieveData ( ap , this . _instanceCancellation ) ;
48
56
this . ActionImageChanged ( ap ) ;
57
+ DataCache . TryRemove ( ap , out var _ ) ;
49
58
}
59
+
50
60
this . Timer . AutoReset = true ;
51
61
this . Timer . Enabled = true ;
62
+ this . _instanceCancellation = new CancellationToken ( false ) ;
52
63
}
53
64
54
65
protected override BitmapImage GetCommandImage ( String actionParameter , PluginImageSize imageSize )
55
66
{
56
67
if ( string . IsNullOrEmpty ( actionParameter ) )
68
+ {
57
69
return null ;
70
+ }
58
71
59
72
Data data = this . GetData ( actionParameter ) ;
60
73
if ( ! data . IsValid )
74
+ {
61
75
return null ;
76
+ }
62
77
63
78
var iconBuilder = new BitmapBuilder ( imageSize ) ;
64
79
iconBuilder . Clear ( BitmapColor . Black ) ;
65
80
66
81
if ( data . Icon != null )
82
+ {
67
83
iconBuilder . DrawImage ( data . Icon ) ;
84
+ }
68
85
69
86
iconBuilder . FillRectangle ( 0 , 0 , iconBuilder . Width , iconBuilder . Height , new BitmapColor ( 0 , 0 , 0 , 128 ) ) ;
70
87
@@ -74,8 +91,16 @@ protected override BitmapImage GetCommandImage(String actionParameter, PluginIma
74
91
locationName = $ "{ data . Name } \n \u00a0 \n ";
75
92
}
76
93
77
- iconBuilder . DrawText ( $ "{ locationName } { Math . Round ( data . Temperature . C ) } °C/{ Math . Round ( data . Temperature . F ) } °F", color : BitmapColor . Black , fontSize : 17 ) ;
78
- iconBuilder . DrawText ( $ "{ locationName } { Math . Round ( data . Temperature . C ) } °C/{ Math . Round ( data . Temperature . F ) } °F", color : BitmapColor . White ) ;
94
+ if ( data . Temperature . C != data . Temperature . F )
95
+ {
96
+ iconBuilder . DrawText ( $ "{ locationName } { Math . Round ( data . Temperature . C ) } °C/{ Math . Round ( data . Temperature . F ) } °F", color : BitmapColor . Black , fontSize : 17 ) ;
97
+ iconBuilder . DrawText ( $ "{ locationName } { Math . Round ( data . Temperature . C ) } °C/{ Math . Round ( data . Temperature . F ) } °F", color : BitmapColor . White ) ;
98
+ }
99
+ else
100
+ {
101
+ iconBuilder . DrawText ( $ "PARAMETER\n ERROR", color : BitmapColor . Black , fontSize : 17 ) ;
102
+ iconBuilder . DrawText ( $ "PARAMETER\n ERROR", color : BitmapColor . White ) ;
103
+ }
79
104
80
105
var renderedImage = iconBuilder . ToImage ( ) ;
81
106
iconBuilder . Dispose ( ) ;
@@ -85,15 +110,19 @@ protected override BitmapImage GetCommandImage(String actionParameter, PluginIma
85
110
86
111
protected override void RunCommand ( String actionParameter ) => System . Diagnostics . Process . Start ( $ "weather:{ actionParameter . Split ( ':' ) [ 0 ] } ") ;
87
112
88
- private async void RetrieveData ( string actionParameter )
113
+ private async void RetrieveData ( string actionParameter , CancellationToken cancellationToken )
89
114
{
90
115
if ( string . IsNullOrEmpty ( actionParameter ) )
116
+ {
91
117
return ;
118
+ }
92
119
93
120
Data data = this . GetData ( actionParameter ) ;
94
121
95
122
if ( data . IsLoading )
123
+ {
96
124
return ;
125
+ }
97
126
98
127
data . IsLoading = true ;
99
128
@@ -108,22 +137,19 @@ private async void RetrieveData(string actionParameter)
108
137
data . HideName = bool . Parse ( paramArgs [ 2 ] ?? "false" ) ;
109
138
}
110
139
111
- Models . Weather weather = null ;
112
- try
113
- {
114
- weather = await this . weatherApiService . GetCurrentWeather ( locationQuery , apiKey ) ;
115
- }
116
- catch ( Exception ex )
140
+ var weather = await this . _weatherApiService . GetCurrentWeather ( locationQuery , apiKey , cancellationToken ) ;
141
+ if ( weather == null )
117
142
{
143
+ // Data is from an older version, clearing.
144
+ data . Name = "ERR" ;
145
+ data . Temperature = ( C : 0 , F : 0 ) ;
118
146
return ;
119
147
}
120
148
121
- data . Name = weather . location . name ;
122
- data . Temperature = ( C : weather . current . temp_c , F : weather . current . temp_f ) ;
123
-
124
- var iconBytes = await this . weatherApiService . GetIconBytes ( weather ) ;
125
- data . Icon = BitmapImage . FromBase64String ( Convert . ToBase64String ( iconBytes ) ) ;
149
+ data . Name = weather . name ;
150
+ data . Temperature = ( C : weather . main . celsius , F : weather . main . fahrenheit ) ;
126
151
152
+ data . Icon = BitmapImage . FromBase64String ( Convert . ToBase64String ( weather . weather [ 0 ] . iconBytes ) ) ;
127
153
}
128
154
finally
129
155
{
@@ -135,13 +161,15 @@ private async void RetrieveData(string actionParameter)
135
161
136
162
private Data GetData ( string actionParameter )
137
163
{
138
- if ( this . DataCache . TryGetValue ( actionParameter , out var data ) )
164
+ if ( DataCache . TryGetValue ( actionParameter , out var data ) )
165
+ {
139
166
return data ;
167
+ }
140
168
141
169
data = new Data ( ) ;
142
- this . DataCache . Add ( actionParameter , data ) ;
143
-
144
- this . RetrieveData ( actionParameter ) ;
170
+ DataCache . TryAdd ( actionParameter , data ) ;
171
+
172
+ this . RetrieveData ( actionParameter , this . _instanceCancellation ) ;
145
173
146
174
return data ;
147
175
}
0 commit comments