@@ -163,10 +163,66 @@ def get_sensors():
163163
164164BASE_URL = 'https://air.siemreap.cloud'
165165
166+ def _aqi_comparison ():
167+ """Query InfluxDB for period-over-period AQI averages.
168+ Returns dict keyed by 'day','week','month' with (direction, delta_pct) tuples.
169+ direction is 'up', 'down', 'similar', or None (insufficient data).
170+ """
171+ measurement , _ = INFLUX_ENTITIES ['aqi' ]
172+ periods = [
173+ ('day' , '-24h' , '-48h' , '-24h' ),
174+ ('week' , '-7d' , '-14d' , '-7d' ),
175+ ('month' , '-30d' , '-60d' , '-30d' ),
176+ ]
177+ results = {}
178+ try :
179+ with InfluxDBClient (url = INFLUX_URL , token = INFLUX_TOKEN , org = INFLUX_ORG ) as client :
180+ qapi = client .query_api ()
181+ for period , curr_start , prev_start , prev_stop in periods :
182+ curr_q = f'''from(bucket: "{ INFLUX_BUCKET } ")
183+ |> range(start: { curr_start } )
184+ |> filter(fn: (r) => r["_measurement"] == "{ measurement } ")
185+ |> filter(fn: (r) => r["_field"] == "value")
186+ |> mean()'''
187+ prev_q = f'''from(bucket: "{ INFLUX_BUCKET } ")
188+ |> range(start: { prev_start } , stop: { prev_stop } )
189+ |> filter(fn: (r) => r["_measurement"] == "{ measurement } ")
190+ |> filter(fn: (r) => r["_field"] == "value")
191+ |> mean()'''
192+ curr_val = next ((r .get_value () for t in qapi .query (curr_q ) for r in t .records ), None )
193+ prev_val = next ((r .get_value () for t in qapi .query (prev_q ) for r in t .records ), None )
194+ if curr_val is None or prev_val is None or prev_val == 0 :
195+ results [period ] = (None , None )
196+ else :
197+ pct = (curr_val - prev_val ) / prev_val * 100
198+ if abs (pct ) < 2 :
199+ results [period ] = ('similar' , None )
200+ elif pct > 0 :
201+ results [period ] = ('up' , round (abs (pct )))
202+ else :
203+ results [period ] = ('down' , round (abs (pct )))
204+ except Exception as e :
205+ app .logger .error (f'AQI comparison error: { e } ' )
206+ for period , * _ in periods :
207+ results .setdefault (period , (None , None ))
208+ return results
209+
210+
166211@app .route ('/metric/<entity_key>/' )
167212def metric (entity_key ):
168213 """Serve the metric history graph page with server-rendered OG tags."""
169214 meta = METRIC_META .get (entity_key , {'label' : 'Air Quality' , 'description' : 'Live air quality readings for Siem Reap, Cambodia.' })
215+
216+ aqi_day_dir = aqi_day_delta = None
217+ aqi_week_dir = aqi_week_delta = None
218+ aqi_month_dir = aqi_month_delta = None
219+
220+ if entity_key == 'aqi' :
221+ cmp = _aqi_comparison ()
222+ aqi_day_dir , aqi_day_delta = cmp .get ('day' , (None , None ))
223+ aqi_week_dir , aqi_week_delta = cmp .get ('week' , (None , None ))
224+ aqi_month_dir , aqi_month_delta = cmp .get ('month' , (None , None ))
225+
170226 template_path = os .path .join (app .root_path , 'static' , 'metric' , 'index.html' )
171227 with open (template_path ) as f :
172228 template_str = f .read ()
@@ -176,6 +232,12 @@ def metric(entity_key):
176232 og_description = meta ['description' ],
177233 og_url = f"{ BASE_URL } /metric/{ entity_key } /" ,
178234 og_image = f"{ BASE_URL } /images/og-{ entity_key } .png" ,
235+ aqi_day_direction = aqi_day_dir ,
236+ aqi_day_delta = aqi_day_delta ,
237+ aqi_week_direction = aqi_week_dir ,
238+ aqi_week_delta = aqi_week_delta ,
239+ aqi_month_direction = aqi_month_dir ,
240+ aqi_month_delta = aqi_month_delta ,
179241 )
180242
181243
0 commit comments