diff --git a/__pycache__/get_rpc_config_auto.cpython-310.pyc b/__pycache__/get_rpc_config_auto.cpython-310.pyc index 4b0576e..b3d2f0f 100644 Binary files a/__pycache__/get_rpc_config_auto.cpython-310.pyc and b/__pycache__/get_rpc_config_auto.cpython-310.pyc differ diff --git a/newpower2.py b/newpower2.py index 17562dd..0cb7212 100644 --- a/newpower2.py +++ b/newpower2.py @@ -447,12 +447,86 @@ class GwtRpcProvider(BaseProvider): if not b: return True return b['lat_min'] <= lat <= b['lat_max'] and b['lon_min'] <= lon <= b['lon_max'] +class NiscHostedProvider(BaseProvider): + """ + Handles NISC Cloud Coop format (JSON with PROJ coordinate strings). + Example: Buckeye REC + """ + def __init__(self, config, session): + super().__init__(config, session) + self.transformer = None + proj_str = config.get('proj_string') + + if proj_str: + try: + # Create transformer from the custom PROJ string to WGS84 + # always_xy=True ensures we assume (Lon, Lat) output order + self.transformer = Transformer.from_proj(proj_str, "EPSG:4326", always_xy=True) + except Exception as e: + logger.error(f"Failed to initialize projection for {self.name}: {e}") + + def fetch(self, is_retry=False): + url = self.config.get('url') + try: + resp = self.session.get(url, verify=False) + if not resp.ok: + logger.error(f"{self.name} HTTP {resp.status_code}") + return [] + + data = resp.json() + outages_list = data.get('outages', []) + + results = [] + for item in outages_list: + results.append(self._normalize(item)) + return results + except Exception as e: + logger.error(f"Error fetching {self.name}: {e}") + return [] + + def _normalize(self, item): + # Coordinates + x, y = item.get('x'), item.get('y') + lat, lon = None, None + + if x is not None and y is not None and self.transformer: + try: + # Transform custom projection X/Y to Lon/Lat + lon, lat = self.transformer.transform(x, y) + except: pass + + # Timestamps (Epoch Milliseconds) + start_ts = None + time_off = item.get('timeOff') + if time_off: + try: + # Convert ms to seconds + start_ts = datetime.fromtimestamp(time_off / 1000, tz=timezone.utc) + except: pass + + return { + 'incidentid': str(item.get('id')), + 'utility': self.name, + 'lat': lat, + 'lon': lon, + 'pointgeom': f"{lat},{lon}" if lat else None, + 'areageom': None, + 'start': start_ts, + 'etr': None, + 'outagen': item.get('nbrOut', 1), + 'cause': "Unknown", + 'crew_status': "Unknown", + 'active': True, + 'last_change': datetime.now(timezone.utc) + } # --- REGISTRY --- PROVIDER_REGISTRY = { 'kubra': KubraProvider, 'simple_json': SimpleJsonProvider, - 'gwt_rpc': GwtRpcProvider + 'gwt_rpc': GwtRpcProvider, + 'nisc_hosted': NiscHostedProvider, + } # --- MAIN --- diff --git a/providers.json b/providers.json index b51a148..fadc234 100644 --- a/providers.json +++ b/providers.json @@ -217,5 +217,11 @@ } ], "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" + }, + { + "name": "Buckeye REC", + "type": "nisc_hosted", + "url": "https://outagemap-data.cloud.coop/buckeyerec/Hosted_Outage_Map/summary.json", + "proj_string": "+proj=lcc +lat_1=40.03333333333333 +lat_2=38.73333333333333 +lat_0=38.73333333333333 +lon_0=-82.648 +x_0=0 +y_0=0 +ellps=GRS80 +datum=NAD83 +to_meter=0.3048006096012192 +no_defs" } ] \ No newline at end of file