changeset 1729:41394bc1d1b0

scrape satellites too, since they reveal what devs are connected to them
author drewp@bigasterisk.com
date Fri, 30 Jun 2023 22:04:36 -0700
parents 81aa0873b48d
children 5d7ce02fa8b2
files service/wifi/scrape.py
diffstat 1 files changed, 64 insertions(+), 18 deletions(-) [+]
line wrap: on
line diff
--- a/service/wifi/scrape.py	Fri Jun 30 22:03:55 2023 -0700
+++ b/service/wifi/scrape.py	Fri Jun 30 22:04:36 2023 -0700
@@ -3,10 +3,10 @@
 import logging
 import re
 import time
-from typing import Awaitable, Callable, Iterable, List
+from typing import Awaitable, Callable, Iterable, List, cast
 
-from cyclone.httpclient import fetch
-from rdflib import Graph, Literal, Namespace, RDF, RDFS, URIRef
+import aiohttp
+from rdflib import RDF, RDFS, Graph, Literal, Namespace, URIRef
 
 log = logging.getLogger()
 ROOM = Namespace("http://projects.bigasterisk.com/room/")
@@ -46,22 +46,66 @@
         raise NotImplementedError(cls)
 
 
-async def loadOrbiData(config: Graph) -> List[SeenNode]:
-    user = config.value(ROOM['wifiScraper'], ROOM['user'])
-    passwd = config.value(ROOM['wifiScraper'], ROOM['password'])
+async def fetch(url, user, passwd) -> str:
     basicAuth = '%s:%s' % (user, passwd)
     headers = {
-        b'Authorization': [b'Basic %s' % base64.encodebytes(basicAuth.encode('utf8')).strip()],
+        'Authorization': 'Basic %s' % base64.encodebytes(basicAuth.encode('ascii')).strip().decode('ascii'),
     }
-    uri = config.value(ROOM['wifiScraper'], ROOM['deviceInfoPage'])
-    resp = await fetch(f"{uri}?ts={time.time()}".encode('utf8'), method=b'GET', headers=headers)
+    async with aiohttp.ClientSession() as session:
+        async with session.get(url, headers=headers) as response:
+            if response.status != 200:
+                raise ValueError(f'{response.status=}')
+
+            return await response.text()
+
+
+async def loadConnectedMacsFromSatellites(satIp, user, passwd):
+    body = await fetch(f'http://{satIp}/refresh_dev.aspx', user, passwd)
+    j = json.loads(body)
+    out = []
+    for row in j['device']:
+        out.append({'mac': row['mac'], 'type': row['type']})
+    return out
+
+
+def findSatellites(satMacs, jrows):
+    satIps = []
+    for row in jrows:
+        mac = row['mac'].lower()
+        for label, satMac in satMacs:
+            if mac == satMac:
+                satIps.append((label, row['ip']))
+    return satIps
+
 
-    if not resp.body.startswith((b'device=', b'device_changed=0\ndevice=', b'device_changed=1\ndevice=')):
-        raise ValueError(resp.body)
+async def loadOrbiData(config: Graph) -> List[SeenNode]:
+
+    def confStr(s, p) -> str:
+        return cast(Literal, config.value(s, p)).toPython()
+
+    user = confStr(ROOM['wifiScraper'], ROOM['user'])
+    passwd = confStr(ROOM['wifiScraper'], ROOM['password'])
+    uri = confStr(ROOM['wifiScraper'], ROOM['deviceInfoPage'])
+    satelliteNamesAndMacs = [
+        (confStr(s, RDFS.label), confStr(s, ROOM['mac']))  #
+        for s in config.objects(ROOM['wifiScraper'], ROOM['satellite'])
+    ]
+    body = await fetch(f"{uri}?ts={time.time()}", user, passwd)
 
-    
-    rows = []
-    for rowNum, row in enumerate(json.loads(resp.body.split(b'device=', 1)[-1])):        
+    if not body.startswith(('device=', 'device_changed=0\ndevice=', 'device_changed=1\ndevice=')):
+        raise ValueError(body)
+
+    outNodes = []
+
+    orbiReportRows = json.loads(body.split('device=', 1)[-1])
+    satelliteNamesAndIps = findSatellites(satelliteNamesAndMacs, orbiReportRows)
+
+    satNameForMac = {}
+    for sat, satIp in satelliteNamesAndIps:
+        for row in await loadConnectedMacsFromSatellites(satIp, user, passwd):
+            satNameForMac[row['mac'].lower()] = ROOM[sat]
+
+    for rowNum, row in enumerate(orbiReportRows):
         log.debug('response row [%d] %r', rowNum, row)
         if not re.match(r'\w\w:\w\w:\w\w:\w\w:\w\w:\w\w', row['mac']):
             raise ValueError(f"corrupt response: mac was {row['mac']!r}")
@@ -71,7 +115,7 @@
         if row['contype'] in ['2.4G', '5G']:
             orbi = macUri(row['conn_orbi_mac'])
             ct = ROOM['wifiBand/%s' % row['contype']]
-            triples.add((uri, ROOM['connectedToAp'], orbi))
+            # triples.add((uri, ROOM['connectedToAp'], orbi)) # always reports the RBR50, i think
             triples.add((uri, ROOM['wifiBand'], ct))
             triples.add((orbi, RDF.type, ROOM['AccessPoint']))
             triples.add((orbi, ROOM['wifiBand'], ct))
@@ -85,10 +129,12 @@
             pass
         else:
             pass
-        triples.add((uri, ROOM['connectedToNet'], ROOM['HouseOpenNet']))
+        triples.add((uri, ROOM['connectedToNet'], ROOM['HouseNet']))
+        if sat := satNameForMac.get(row['mac'].lower()):
+            triples.add((uri, ROOM['connectedToAp'], sat))
 
         if row['model'] != 'Unknown':
             triples.add((uri, ROOM['networkModel'], Literal(row['model'])))
 
-        rows.append(SeenNode(uri=uri, mac=row['mac'].lower(), ip=row['ip'], stmts=triples))
-    return rows
+        outNodes.append(SeenNode(uri=uri, mac=row['mac'].lower(), ip=row['ip'], stmts=triples))
+    return outNodes