Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
5289779b9This file is pinned to the selected release. Download the original to open it in your design tool.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="../global.css">
<link rel="stylesheet" href="preference.css">
<script type="module">
import SimpleBLEClient from './ble-client.mjs'
document.addEventListener('DOMContentLoaded', () => {
/**
* Views
*/
const settingsForm = document.getElementById('settings-form')
const bleConnectButton = document.getElementById('ble-connect-button')
const updateView = () => {
if (client.isConnected()) {
bleConnectButton.classList.remove('active')
settingsForm.classList.add('active')
} else {
settingsForm.classList.remove('active')
bleConnectButton.classList.add('active')
}
}
/**
* Event Handlers
*/
const handleConnectButtonClick = async () => {
console.log("Connecting")
await client.connect()
updateView()
}
const handleDisconnectButtonClick = async () => {
console.log("Disconnecting")
await client.disconnect()
updateView()
}
const toast = document.getElementById('toast')
toast.addEventListener('animationend', () => {
toast.className = 'toast'
})
const showToast = () => {
toast.classList.add('visible')
}
let submitting = null
const handleWifiSubmit = async (event) => {
console.log('handlewifi')
event.preventDefault();
if (!client.isConnected()) {
console.warn('Not Connected')
return;
}
const props = [
'wifi.ssid',
'wifi.password',
'driver.type',
'driver.offsetPan',
'driver.offsetTilt',
'renderer.type',
'tts.type',
'tts.host',
'tts.port',
'tts.token',
'tts.voice',
'tts.volume',
'ai.token',
'ai.context',
]
const payload = {}
for (const prop of props) {
const value = document.getElementById(prop)?.value
if (value != null && value.trimEnd().length > 0) {
payload[prop] = value
}
}
await client.send({ _batch: payload })
console.log('Sent')
submitting = setTimeout(() => {
console.error('Timeout')
}, 3000)
}
const handleCharacteristicWritten = ({ prop, value }) => {
if (submitting != null) {
toast.innerText = 'Preference set [・_・]'
showToast()
clearTimeout(submitting)
submitting = null
}
const elem = document.querySelector(`[name="${prop}"]`)
if (elem != null) {
elem.value = value
}
}
/**
* BLE Client
*/
const client = new SimpleBLEClient({
deviceName: "STK",
onCharacteristicValueChanged: handleCharacteristicWritten
})
client.onDisconnected = () => {
updateView()
}
/**
* Bind
*/
document.getElementById('ble-connect-button').addEventListener('click', handleConnectButtonClick)
document.getElementById('ble-disconnect-button').addEventListener('click', handleDisconnectButtonClick)
document.getElementById('form-preference').addEventListener('submit', handleWifiSubmit)
})
</script>
<title>Document</title>
</head>
<body>
<div class="app">
<div>
<button class="accordion active" id="ble-connect-button">
Connect Stack-chan with BLE[・_・]
</button>
</div>
<div class="accordion card" id="settings-form">
<h2>Stack-chan preferences</h2>
<div class="ble-disconnect-button" id="ble-disconnect-button" title="Disconnect">×</div>
<form class="form" id="form-preference">
<h3>WiFi</h3>
<div class="form-group">
<label for="wifi.ssid">SSID: </label>
<input type="text" name="wifi.ssid" id="wifi.ssid" autocomplete="off">
</div>
<div class="form-group">
<label for="wifi.password">Password: </label>
<input type="password" name="wifi.password" id="wifi.password" autocomplete="off">
</div>
<h3>Face</h3>
<div class="form-group">
<label for="renderer.type">Type: </label>
<select name="renderer.type" id="renderer.type">
<option value="simple">Simple</option>
<option value="dog">Dog</option>
</select>
</div>
<h3>Servo</h3>
<div class="form-group">
<label for="driver.type">Type: </label>
<select name="driver.type" id="driver.type">
<option value="scservo">SCServo</option>
<option value="dynamixel">Dynamixel(Protocol version 2)</option>
<option value="rs30x">RS30X</option>
<option value="pwm">PWM(SG-90)</option>
<option value="none">None</option>
</select>
</div>
<div class="form-group">
<label for="driver.offsetPan">Offset Pan:</label>
<input type="number" name="driver.offsetPan" id="driver.offsetPan" value="0">
</div>
<div class="form-group">
<label for="driver.offsetTilt">Offset Tilt:</label>
<input type="number" name="driver.offsetTilt" id="driver.offsetTilt" value="0">
</div>
<h3>TTS</h3>
<div class="form-group">
<label for="tts.type">Type: </label>
<select name="tts.type" id="tts.type">
<option value="voicevox">VOICEVOX</option>
<option value="elevenlabs">ElevenLabs</option>
<option value="google-tts">Google TTS</option>
<option value="openai">OpenAI</option>
<option value="local">Local</option>
</select>
</div>
<div class="form-group">
<label for="tts.host">Host:</label>
<input type="text" name="tts.host" id="tts.host" placeholder="my-tts-host.local">
</div>
<div class="form-group">
<label for="tts.port">Port:</label>
<input type="number" name="tts.port" id="tts.port" placeholder="50021">
</div>
<div class="form-group">
<label for="tts.voice">Voice:</label>
<input type="text" name="tts.voice" id="tts.voice" placeholder="ally">
</div>
<div class="form-group">
<label for="tts.token">Token:</label>
<input type="text" name="tts.token" id="tts.token">
</div>
<div class="form-group">
<label for="tts.volume">Volume:</label>
<input type="number" name="tts.volume" id="tts.volume" step="0.1" min="0" max="1">
</div>
<h3>AI</h3>
<div class="form-group">
<label for="ai.token">Token:</label>
<input type="text" name="ai.token" id="ai.token">
</div>
<label for="ai.context">System Role:</label>
<textarea name="ai.context" id="ai.context" cols="30" rows="5"
placeholder="You are Stack-chan(スタックチャン), the palm sized super kawaii companion robot."></textarea>
<hr>
<button type="submit">Submit</button>
</form>
</div>
</div>
<div id="toast" class="toast"></div>
</body>
</html>