|
14 | 14 |
|
15 | 15 | from electricitymap.contrib.config import ZoneKey
|
16 | 16 | from electricitymap.contrib.lib.models.event_lists import (
|
| 17 | + GridAlertList, |
17 | 18 | ProductionBreakdownList,
|
18 | 19 | TotalConsumptionList,
|
19 | 20 | )
|
20 |
| -from electricitymap.contrib.lib.models.events import EventSourceType, ProductionMix |
| 21 | +from electricitymap.contrib.lib.models.events import ( |
| 22 | + EventSourceType, |
| 23 | + GridAlertType, |
| 24 | + ProductionMix, |
| 25 | +) |
21 | 26 |
|
22 | 27 | SOURCE = "misoenergy.org"
|
23 | 28 | ZONE = "US-MIDW-MISO"
|
@@ -217,11 +222,70 @@ def fetch_wind_solar_forecasts(
|
217 | 222 | return production_breakdowns.to_list()
|
218 | 223 |
|
219 | 224 |
|
| 225 | +def fetch_grid_alerts( |
| 226 | + zone_key: ZoneKey = ZoneKey(ZONE), |
| 227 | + session: Session | None = None, |
| 228 | + target_datetime: datetime | None = None, |
| 229 | + logger: Logger = getLogger(__name__), |
| 230 | +) -> list[dict[str, Any]]: |
| 231 | + """Fetch Grid Alerts from MISO""" |
| 232 | + session = session or Session() |
| 233 | + |
| 234 | + # API URL |
| 235 | + url = "https://www.misoenergy.org/api/topicnotifications/getrecentnotifications" |
| 236 | + |
| 237 | + # Request payload (can be adjusted if you want specific filters) |
| 238 | + payload = {"topics": ["RealTime"], "take": 0} # take 0 is equal to get all possible |
| 239 | + # TODO: do we just need the last one? |
| 240 | + |
| 241 | + # Request headers |
| 242 | + headers = { |
| 243 | + "Content-Type": "application/json", |
| 244 | + "Accept": "application/json", |
| 245 | + "User-Agent": "Mozilla/5.0", |
| 246 | + "Referer": "https://www.misoenergy.org/markets-and-operations/notifications/real-time-operations-notifications/", |
| 247 | + "X-Requested-With": "XMLHttpRequest", |
| 248 | + } |
| 249 | + |
| 250 | + # Make the POST request |
| 251 | + response = session.post(url, json=payload, headers=headers) |
| 252 | + |
| 253 | + # Check for success |
| 254 | + if response.status_code == 200: |
| 255 | + notifications = response.json() |
| 256 | + |
| 257 | + # TODO: maybe extract locationRegion from each notification? |
| 258 | + # TODO: maybe extract startTime and endTime from each notification? |
| 259 | + # TODO: maybe extract alertType from each notification? |
| 260 | + |
| 261 | + # Record events in grid_alert_list |
| 262 | + grid_alert_list = GridAlertList(logger) |
| 263 | + for notification in notifications: |
| 264 | + publish_datetime = datetime.fromisoformat( |
| 265 | + notification["publishDateUnformatted"] |
| 266 | + ) # in UTC |
| 267 | + grid_alert_list.append( |
| 268 | + zoneKey=zone_key, |
| 269 | + locationRegion=None, |
| 270 | + source=SOURCE, |
| 271 | + alertType=GridAlertType.undefined, |
| 272 | + message=notification["subject"] + "\n" + notification["body"], |
| 273 | + issuedTime=publish_datetime, |
| 274 | + startTime=None, # if None, it defaults to issuedTime |
| 275 | + endTime=None, |
| 276 | + ) |
| 277 | + return grid_alert_list.to_list() |
| 278 | + |
| 279 | + |
220 | 280 | if __name__ == "__main__":
|
221 |
| - # print("fetch_production() ->") |
222 |
| - # print(fetch_production()) |
| 281 | + from pprint import pprint |
| 282 | + |
| 283 | + print("fetch_production() ->") |
| 284 | + print(fetch_production()) |
223 | 285 |
|
224 | 286 | print(fetch_consumption_forecast())
|
225 | 287 |
|
226 |
| - # print("fetch_wind_solar_forecasts() ->") |
227 |
| - # print(fetch_wind_solar_forecasts()) |
| 288 | + print("fetch_wind_solar_forecasts() ->") |
| 289 | + print(fetch_wind_solar_forecasts()) |
| 290 | + |
| 291 | + pprint(fetch_grid_alerts()) |
0 commit comments