feat: availability broadcast (Kind 30078) + WiFi auto-connect
Wire up the availability broadcast composable to publish the ATM's status as a replaceable Kind 30078 Nostr event. Publishes on availability change (debounced) and as a 5-minute heartbeat so monitors can detect offline machines. Also adds WiFi auto-connect for BATM3: reads SSID/PSK from /var/lib/lamassu-atm/wifi.conf at boot. Credentials stay local. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
7afcc6fc5d
commit
67c7c12ade
3 changed files with 82 additions and 1 deletions
|
|
@ -93,9 +93,22 @@ export function useAvailabilityBroadcast(options: UseAvailabilityBroadcastOption
|
||||||
{ deep: true }
|
{ deep: true }
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Heartbeat: republish every 5 minutes so monitors know the machine is alive.
|
||||||
|
// If the latest Kind 30078 event is older than ~10 minutes, the machine is offline.
|
||||||
|
const heartbeatInterval = setInterval(
|
||||||
|
() => {
|
||||||
|
publish(computeAvailability())
|
||||||
|
},
|
||||||
|
5 * 60 * 1000
|
||||||
|
)
|
||||||
|
|
||||||
// Publish immediately on setup
|
// Publish immediately on setup
|
||||||
const initial = computeAvailability()
|
const initial = computeAvailability()
|
||||||
publish(initial)
|
publish(initial)
|
||||||
|
|
||||||
return { computeAvailability }
|
function stop() {
|
||||||
|
clearInterval(heartbeatInterval)
|
||||||
|
}
|
||||||
|
|
||||||
|
return { computeAvailability, stop }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import {
|
||||||
type ManagementResponse,
|
type ManagementResponse,
|
||||||
} from '@lamassu/clink'
|
} from '@lamassu/clink'
|
||||||
import type { TransactionRecord } from '@/types/state'
|
import type { TransactionRecord } from '@/types/state'
|
||||||
|
import { useAvailabilityBroadcast } from '@/composables/useAvailabilityBroadcast'
|
||||||
|
|
||||||
// Check if we're running in Electron
|
// Check if we're running in Electron
|
||||||
const isElectron = typeof window !== 'undefined' && window.electronAPI !== undefined
|
const isElectron = typeof window !== 'undefined' && window.electronAPI !== undefined
|
||||||
|
|
@ -308,6 +309,10 @@ export const useAtmStore = defineStore('atm', () => {
|
||||||
return snapshot.value?.context ?? null
|
return snapshot.value?.context ?? null
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const inventory = computed((): Record<number, number> => {
|
||||||
|
return context.value?.inventory ?? {}
|
||||||
|
})
|
||||||
|
|
||||||
const fiatSymbol = computed(() => {
|
const fiatSymbol = computed(() => {
|
||||||
const symbols: Record<string, string> = { USD: '$', GTQ: 'Q', EUR: '€', GBP: '£', MXN: '$' }
|
const symbols: Record<string, string> = { USD: '$', GTQ: 'Q', EUR: '€', GBP: '£', MXN: '$' }
|
||||||
return symbols[fiatCode.value] || fiatCode.value
|
return symbols[fiatCode.value] || fiatCode.value
|
||||||
|
|
@ -557,6 +562,9 @@ export const useAtmStore = defineStore('atm', () => {
|
||||||
|
|
||||||
// Initialize state machine with real services
|
// Initialize state machine with real services
|
||||||
initialize(servicesWithInventory)
|
initialize(servicesWithInventory)
|
||||||
|
|
||||||
|
// Start broadcasting availability (Kind 30078) with 5-minute heartbeat
|
||||||
|
startAvailabilityBroadcast(services.nostrClient, services.identity, 'atm')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[ATM] Failed to connect to Lightning.Pub:', error)
|
console.error('[ATM] Failed to connect to Lightning.Pub:', error)
|
||||||
connectionStatus.value = 'error'
|
connectionStatus.value = 'error'
|
||||||
|
|
@ -847,6 +855,9 @@ export const useAtmStore = defineStore('atm', () => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Start broadcasting availability (Kind 30078)
|
||||||
|
startAvailabilityBroadcast(lightning.nostrClient, lightning.identity, 'atm')
|
||||||
|
|
||||||
console.log('[ATM] Fully initialized with HAL + Lightning')
|
console.log('[ATM] Fully initialized with HAL + Lightning')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[ATM] HAL initialization failed:', error)
|
console.error('[ATM] HAL initialization failed:', error)
|
||||||
|
|
@ -1099,6 +1110,9 @@ export const useAtmStore = defineStore('atm', () => {
|
||||||
|
|
||||||
// Real hardware connected — disable mock bill simulator
|
// Real hardware connected — disable mock bill simulator
|
||||||
debugMode.value = false
|
debugMode.value = false
|
||||||
|
// Start broadcasting availability (Kind 30078)
|
||||||
|
startAvailabilityBroadcast(lightning.nostrClient, lightning.identity, 'atm')
|
||||||
|
|
||||||
console.log('[ATM] Fully initialized with HAL (IPC) + Lightning')
|
console.log('[ATM] Fully initialized with HAL (IPC) + Lightning')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[ATM] HAL initialization failed:', error)
|
console.error('[ATM] HAL initialization failed:', error)
|
||||||
|
|
@ -1225,6 +1239,23 @@ export const useAtmStore = defineStore('atm', () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Start broadcasting ATM availability (Kind 30078) with 5-minute heartbeat */
|
||||||
|
let stopAvailabilityBroadcast: (() => void) | null = null
|
||||||
|
|
||||||
|
function startAvailabilityBroadcast(nostrClient: any, identity: any, model: string) {
|
||||||
|
if (stopAvailabilityBroadcast) return
|
||||||
|
|
||||||
|
const { stop } = useAvailabilityBroadcast({
|
||||||
|
nostrClient,
|
||||||
|
identity,
|
||||||
|
inventory,
|
||||||
|
balanceSats,
|
||||||
|
fiatCode: fiatCode.value,
|
||||||
|
model,
|
||||||
|
})
|
||||||
|
stopAvailabilityBroadcast = stop
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// State
|
// State
|
||||||
actor,
|
actor,
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,43 @@
|
||||||
hybrid-sleep.enable = false;
|
hybrid-sleep.enable = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# WiFi: read credentials from /var/lib/lamassu-atm/wifi.conf (not in repo)
|
||||||
|
# Format: SSID=MyNetwork\nPSK=MyPassword
|
||||||
|
system.activationScripts.wifi-setup = ''
|
||||||
|
WIFI_CONF="/var/lib/lamassu-atm/wifi.conf"
|
||||||
|
if [ -f "$WIFI_CONF" ]; then
|
||||||
|
SSID=$(grep '^SSID=' "$WIFI_CONF" | cut -d= -f2-)
|
||||||
|
PSK=$(grep '^PSK=' "$WIFI_CONF" | cut -d= -f2-)
|
||||||
|
if [ -n "$SSID" ] && [ -n "$PSK" ]; then
|
||||||
|
CONN_FILE="/etc/NetworkManager/system-connections/$SSID.nmconnection"
|
||||||
|
if [ ! -f "$CONN_FILE" ]; then
|
||||||
|
cat > "$CONN_FILE" << EOF
|
||||||
|
[connection]
|
||||||
|
id=$SSID
|
||||||
|
type=wifi
|
||||||
|
autoconnect=true
|
||||||
|
|
||||||
|
[wifi]
|
||||||
|
mode=infrastructure
|
||||||
|
ssid=$SSID
|
||||||
|
|
||||||
|
[wifi-security]
|
||||||
|
key-mgmt=wpa-psk
|
||||||
|
psk=$PSK
|
||||||
|
|
||||||
|
[ipv4]
|
||||||
|
method=auto
|
||||||
|
|
||||||
|
[ipv6]
|
||||||
|
method=auto
|
||||||
|
EOF
|
||||||
|
chmod 600 "$CONN_FILE"
|
||||||
|
echo "[WiFi] Created connection for $SSID"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
|
||||||
# eGalax touchscreen (Dell 9030 AIO built-in panel)
|
# eGalax touchscreen (Dell 9030 AIO built-in panel)
|
||||||
# The eGalax HID descriptor confuses libinput (treats it as touchpad).
|
# The eGalax HID descriptor confuses libinput (treats it as touchpad).
|
||||||
# Fix: unbind from usbhid at boot, bind to usbtouchscreen kernel module,
|
# Fix: unbind from usbhid at boot, bind to usbtouchscreen kernel module,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue