1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-04-13 17:22:08 -04:00

weather: fix geoclue IP fallback

This commit is contained in:
bbedward
2026-02-28 00:06:46 -05:00
parent d7c501e175
commit 3600e034b8
3 changed files with 63 additions and 83 deletions

View File

@@ -5,21 +5,38 @@ import "github.com/AvengeMedia/DankMaterialShell/core/internal/log"
func NewClient() Client { func NewClient() Client {
geoclueClient, err := newGeoClueClient() geoclueClient, err := newGeoClueClient()
if err != nil { if err != nil {
log.Warnf("Failed to initialize GeoClue2 client: %v", err) log.Warnf("GeoClue2 unavailable: %v", err)
log.Info("Falling back to IP location") return newSeededIpClient()
return newIpClient()
} }
loc, _ := geoclueClient.GetLocation() loc, _ := geoclueClient.GetLocation()
if loc.Latitude != 0 || loc.Longitude != 0 { if loc.Latitude != 0 || loc.Longitude != 0 {
log.Info("Using GeoClue2 location")
return geoclueClient return geoclueClient
} }
log.Info("GeoClue2 has no fix yet, seeding with IP location") log.Info("GeoClue2 has no fix yet, seeding with IP location")
ipClient := newIpClient() ipLoc, err := fetchIPLocation()
if ipLoc, err := ipClient.GetLocation(); err == nil { if err != nil {
geoclueClient.SeedLocation(ipLoc) log.Warnf("IP location seed failed: %v", err)
return geoclueClient
} }
log.Info("Seeded GeoClue2 with IP location")
geoclueClient.SeedLocation(Location{Latitude: ipLoc.Latitude, Longitude: ipLoc.Longitude})
return geoclueClient return geoclueClient
} }
func newSeededIpClient() *IpClient {
client := newIpClient()
ipLoc, err := fetchIPLocation()
if err != nil {
log.Warnf("IP location also failed: %v", err)
return client
}
log.Info("Using IP location")
client.currLocation.Latitude = ipLoc.Latitude
client.currLocation.Longitude = ipLoc.Longitude
return client
}

View File

@@ -6,26 +6,27 @@ import (
"io" "io"
"net/http" "net/http"
"time" "time"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
) )
type IpClient struct { type IpClient struct {
currLocation *Location currLocation *Location
} }
type ipLocationResult struct {
Location
City string
}
type ipAPIResponse struct { type ipAPIResponse struct {
Lat float64 `json:"lat"` Status string `json:"status"`
Lon float64 `json:"lon"` Lat float64 `json:"lat"`
City string `json:"city"` Lon float64 `json:"lon"`
City string `json:"city"`
} }
func newIpClient() *IpClient { func newIpClient() *IpClient {
return &IpClient{ return &IpClient{
currLocation: &Location{ currLocation: &Location{},
Latitude: 0.0,
Longitude: 0.0,
},
} }
} }
@@ -34,55 +35,57 @@ func (c *IpClient) Subscribe(id string) chan Location {
if location, err := c.GetLocation(); err == nil { if location, err := c.GetLocation(); err == nil {
ch <- location ch <- location
} }
return ch return ch
} }
func (c *IpClient) Unsubscribe(id string) { func (c *IpClient) Unsubscribe(id string) {}
// Stub
}
func (c *IpClient) Close() { func (c *IpClient) Close() {}
// Stub
}
func (c *IpClient) GetLocation() (Location, error) { func (c *IpClient) GetLocation() (Location, error) {
client := &http.Client{ if c.currLocation.Latitude != 0 || c.currLocation.Longitude != 0 {
Timeout: 10 * time.Second, return *c.currLocation, nil
} }
result := Location{ result, err := fetchIPLocation()
Latitude: 0.0, if err != nil {
Longitude: 0.0, return Location{}, err
} }
c.currLocation.Latitude = result.Latitude
c.currLocation.Longitude = result.Longitude
return *c.currLocation, nil
}
func fetchIPLocation() (ipLocationResult, error) {
client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Get("http://ip-api.com/json/") resp, err := client.Get("http://ip-api.com/json/")
if err != nil { if err != nil {
return result, fmt.Errorf("failed to fetch IP location: %w", err) return ipLocationResult{}, fmt.Errorf("failed to fetch IP location: %w", err)
} }
defer resp.Body.Close() defer resp.Body.Close()
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
return result, fmt.Errorf("ip-api.com returned status %d", resp.StatusCode) return ipLocationResult{}, fmt.Errorf("ip-api.com returned status %d", resp.StatusCode)
} }
body, err := io.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return result, fmt.Errorf("failed to read response: %w", err) return ipLocationResult{}, fmt.Errorf("failed to read response: %w", err)
} }
var data ipAPIResponse var data ipAPIResponse
if err := json.Unmarshal(body, &data); err != nil { if err := json.Unmarshal(body, &data); err != nil {
return result, fmt.Errorf("failed to parse response: %w", err) return ipLocationResult{}, fmt.Errorf("failed to parse response: %w", err)
} }
if data.Lat == 0 && data.Lon == 0 { if data.Status == "fail" || (data.Lat == 0 && data.Lon == 0) {
return result, fmt.Errorf("missing location data in response") return ipLocationResult{}, fmt.Errorf("ip-api.com returned no location data")
} }
log.Infof("Fetched IP-based location: %s (%.4f, %.4f)", data.City, data.Lat, data.Lon) return ipLocationResult{
result.Latitude = data.Lat Location: Location{Latitude: data.Lat, Longitude: data.Lon},
result.Longitude = data.Lon City: data.City,
}, nil
return result, nil
} }

View File

@@ -17,11 +17,9 @@ Singleton {
signal locationChanged(var data) signal locationChanged(var data)
readonly property var lowPriorityCmd: ["nice", "-n", "19", "ionice", "-c3"] onLocationAvailableChanged: {
readonly property var curlBaseCmd: ["curl", "-sS", "--fail", "--connect-timeout", "3", "--max-time", "6", "--limit-rate", "100k", "--compressed"] if (locationAvailable && !valid)
getState();
Component.onCompleted: {
getState();
} }
Connections { Connections {
@@ -46,50 +44,12 @@ Singleton {
} }
function getState() { function getState() {
if (!locationAvailable) { if (!locationAvailable)
fetchIPLocation();
return; return;
}
DMSService.sendRequest("location.getState", null, response => { DMSService.sendRequest("location.getState", null, response => {
if (response.result && (response.result.latitude !== 0 || response.result.longitude !== 0)) { if (response.result)
handleStateUpdate(response.result); handleStateUpdate(response.result);
return;
}
fetchIPLocation();
}); });
} }
function fetchIPLocation() {
if (root.valid)
return;
ipLocationFetcher.running = true;
}
Process {
id: ipLocationFetcher
command: root.lowPriorityCmd.concat(root.curlBaseCmd).concat(["http://ip-api.com/json/"])
running: false
stdout: StdioCollector {
onStreamFinished: {
const raw = text.trim();
if (!raw || raw[0] !== "{")
return;
try {
const data = JSON.parse(raw);
if (data.status === "fail")
return;
const lat = parseFloat(data.lat);
const lon = parseFloat(data.lon);
if (isNaN(lat) || isNaN(lon) || (lat === 0 && lon === 0))
return;
root.handleStateUpdate({ latitude: lat, longitude: lon });
} catch (e) {}
}
}
}
} }