velconnect v2 wow

dev
Anton Franzluebbers 2022-07-05 20:01:45 -04:00
parent 4502662a53
commit 5762013f03
7 changed files with 501 additions and 135 deletions

1
.gitignore vendored
View File

@ -11,4 +11,5 @@ discord_bot/graph.png
discord_bot/config.py discord_bot/config.py
env_win/ env_win/
velconnect.db velconnect.db
velconnect*.db
.idea/ .idea/

View File

@ -14,7 +14,7 @@ namespace VELConnect
public class VELConnectManager : MonoBehaviour public class VELConnectManager : MonoBehaviour
{ {
public string velConnectUrl = "http://localhost"; public string velConnectUrl = "http://localhost";
public static VELConnectManager instance; private static VELConnectManager instance;
public class State public class State
{ {
@ -36,7 +36,8 @@ namespace VELConnect
/// </summary> /// </summary>
public string TryGetData(string key) public string TryGetData(string key)
{ {
return data?.TryGetValue(key, out string val) == true ? val : null; string val = null;
return data?.TryGetValue(key, out val) == true ? val : null;
} }
} }
@ -50,6 +51,15 @@ namespace VELConnect
public string last_modified; public string last_modified;
public string last_accessed; public string last_accessed;
public Dictionary<string, string> data; public Dictionary<string, string> data;
/// <summary>
/// Returns the value if it exists, otherwise null
/// </summary>
public string TryGetData(string key)
{
string val = null;
return data?.TryGetValue(key, out val) == true ? val : null;
}
} }
public Device device; public Device device;
@ -63,6 +73,25 @@ namespace VELConnect
public static Action<string, string> OnDeviceDataChanged; public static Action<string, string> OnDeviceDataChanged;
public static Action<string, string> OnRoomDataChanged; public static Action<string, string> OnRoomDataChanged;
private static readonly Dictionary<string, List<CallbackListener>> deviceFieldCallbacks = new Dictionary<string, List<CallbackListener>>();
private static readonly Dictionary<string, List<CallbackListener>> deviceDataCallbacks = new Dictionary<string, List<CallbackListener>>();
private static readonly Dictionary<string, List<CallbackListener>> roomDataCallbacks = new Dictionary<string, List<CallbackListener>>();
private struct CallbackListener
{
/// <summary>
/// Used so that other objects don't have to remove listeners themselves
/// </summary>
public MonoBehaviour keepAliveObject;
public Action<string> callback;
/// <summary>
/// Sends the first state received from the network or the state at binding time
/// </summary>
public bool sendInitialState;
}
public static int PairingCode public static int PairingCode
{ {
get get
@ -80,10 +109,10 @@ namespace VELConnect
{ {
get get
{ {
#if UNITY_STANDALONE_WIN && !UNITY_EDITOR #if UNITY_EDITOR
// allows running multiple builds on the same computer // allows running multiple builds on the same computer
// return SystemInfo.deviceUniqueIdentifier + Hash128.Compute(Application.dataPath); // return SystemInfo.deviceUniqueIdentifier + Hash128.Compute(Application.dataPath);
return SystemInfo.deviceUniqueIdentifier + "_BUILD"; return SystemInfo.deviceUniqueIdentifier + "_EDITOR";
#else #else
return SystemInfo.deviceUniqueIdentifier; return SystemInfo.deviceUniqueIdentifier;
#endif #endif
@ -137,7 +166,7 @@ namespace VELConnect
{ "version", Application.version }, { "version", Application.version },
{ "platform", SystemInfo.operatingSystem }, { "platform", SystemInfo.operatingSystem },
}; };
PostRequestCallback(velConnectUrl + "/api/v2/update_user_count", JsonConvert.SerializeObject(postData)); PostRequestCallback(velConnectUrl + "/api/update_user_count", JsonConvert.SerializeObject(postData));
}); });
} }
@ -147,11 +176,13 @@ namespace VELConnect
{ {
try try
{ {
GetRequestCallback(velConnectUrl + "/api/v2/get_state/" + DeviceId, json => GetRequestCallback(velConnectUrl + "/api/v2/device/get_data/" + DeviceId, json =>
{ {
State state = JsonConvert.DeserializeObject<State>(json); State state = JsonConvert.DeserializeObject<State>(json);
if (state == null) return; if (state == null) return;
bool isInitialState = false;
// first load stuff // first load stuff
if (lastState == null) if (lastState == null)
{ {
@ -164,8 +195,9 @@ namespace VELConnect
Debug.LogError(e); Debug.LogError(e);
} }
lastState = state; isInitialState = true;
return; // lastState = state;
// return;
} }
@ -176,52 +208,126 @@ namespace VELConnect
foreach (FieldInfo fieldInfo in fields) foreach (FieldInfo fieldInfo in fields)
{ {
string newValue = fieldInfo.GetValue(state.device) as string; string newValue = fieldInfo.GetValue(state.device) as string;
string oldValue = fieldInfo.GetValue(lastState.device) as string; string oldValue = lastState != null ? fieldInfo.GetValue(lastState.device) as string : null;
if (newValue != oldValue) if (newValue != oldValue)
{ {
try try
{ {
OnDeviceFieldChanged?.Invoke(fieldInfo.Name, newValue); if (!isInitialState) OnDeviceFieldChanged?.Invoke(fieldInfo.Name, newValue);
} }
catch (Exception e) catch (Exception e)
{ {
Debug.LogError(e); Debug.LogError(e);
} }
// send specific listeners data
if (deviceFieldCallbacks.ContainsKey(fieldInfo.Name))
{
// clear the list of old listeners
deviceFieldCallbacks[fieldInfo.Name].RemoveAll(e => e.keepAliveObject == null);
// send the callbacks
deviceFieldCallbacks[fieldInfo.Name].ForEach(e =>
{
if (!isInitialState || e.sendInitialState)
{
try
{
e.callback(newValue);
}
catch (Exception ex)
{
Debug.LogError(ex);
}
}
});
}
} }
} }
foreach (KeyValuePair<string, string> elem in state.device.data) if (state.device.data != null)
{ {
lastState.device.data.TryGetValue(elem.Key, out string oldValue); foreach (KeyValuePair<string, string> elem in state.device.data)
if (elem.Value != oldValue)
{ {
try string oldValue = null;
lastState?.device.data.TryGetValue(elem.Key, out oldValue);
if (elem.Value != oldValue)
{ {
OnDeviceDataChanged?.Invoke(elem.Key, elem.Value); try
} {
catch (Exception e) if (!isInitialState) OnDeviceDataChanged?.Invoke(elem.Key, elem.Value);
{ }
Debug.LogError(e); catch (Exception ex)
{
Debug.LogError(ex);
}
// send specific listeners data
if (deviceDataCallbacks.ContainsKey(elem.Key))
{
// clear the list of old listeners
deviceDataCallbacks[elem.Key].RemoveAll(e => e.keepAliveObject == null);
// send the callbacks
deviceDataCallbacks[elem.Key].ForEach(e =>
{
if (!isInitialState || e.sendInitialState)
{
try
{
e.callback(elem.Value);
}
catch (Exception ex)
{
Debug.LogError(ex);
}
}
});
}
} }
} }
} }
} }
if (state.room.modified_by != DeviceId) if (state.room.modified_by != DeviceId && state.room.data != null)
{ {
foreach (KeyValuePair<string, string> elem in state.room.data) foreach (KeyValuePair<string, string> elem in state.room.data)
{ {
lastState.room.data.TryGetValue(elem.Key, out string oldValue); string oldValue = null;
lastState?.room.data.TryGetValue(elem.Key, out oldValue);
if (elem.Value != oldValue) if (elem.Value != oldValue)
{ {
try try
{ {
OnRoomDataChanged?.Invoke(elem.Key, elem.Value); if (!isInitialState) OnRoomDataChanged?.Invoke(elem.Key, elem.Value);
} }
catch (Exception e) catch (Exception e)
{ {
Debug.LogError(e); Debug.LogError(e);
} }
// send specific listeners data
if (roomDataCallbacks.ContainsKey(elem.Key))
{
// clear the list of old listeners
roomDataCallbacks[elem.Key].RemoveAll(e => e.keepAliveObject == null);
// send the callbacks
roomDataCallbacks[elem.Key].ForEach(e =>
{
if (!isInitialState || e.sendInitialState)
{
try
{
e.callback(elem.Value);
}
catch (Exception ex)
{
Debug.LogError(ex);
}
}
});
}
} }
} }
} }
@ -239,16 +345,130 @@ namespace VELConnect
} }
} }
/// <summary>
/// Adds a change listener callback to a particular field name within the Device main fields.
/// </summary>
public static void AddDeviceFieldListener(string key, MonoBehaviour keepAliveObject, Action<string> callback, bool sendInitialState = false)
{
if (!deviceFieldCallbacks.ContainsKey(key))
{
deviceFieldCallbacks[key] = new List<CallbackListener>();
}
deviceFieldCallbacks[key].Add(new CallbackListener()
{
keepAliveObject = keepAliveObject,
callback = callback,
sendInitialState = sendInitialState
});
if (sendInitialState)
{
if (instance != null && instance.lastState?.device != null)
{
if (instance.lastState.device.GetType().GetField(key)?.GetValue(instance.lastState.device) is string val)
{
try
{
callback(val);
}
catch (Exception e)
{
Debug.LogError(e);
}
}
}
}
}
/// <summary>
/// Adds a change listener callback to a particular field name within the Device data JSON.
/// </summary>
public static void AddDeviceDataListener(string key, MonoBehaviour keepAliveObject, Action<string> callback, bool sendInitialState = false)
{
if (!deviceDataCallbacks.ContainsKey(key))
{
deviceDataCallbacks[key] = new List<CallbackListener>();
}
deviceDataCallbacks[key].Add(new CallbackListener()
{
keepAliveObject = keepAliveObject,
callback = callback,
sendInitialState = sendInitialState
});
if (sendInitialState)
{
string val = GetDeviceData(key);
if (val != null)
{
try
{
callback(val);
}
catch (Exception e)
{
Debug.LogError(e);
}
}
}
}
/// <summary>
/// Adds a change listener callback to a particular field name within the Room data JSON.
/// </summary>
public static void AddRoomDataListener(string key, MonoBehaviour keepAliveObject, Action<string> callback, bool sendInitialState = false)
{
if (!roomDataCallbacks.ContainsKey(key))
{
roomDataCallbacks[key] = new List<CallbackListener>();
}
roomDataCallbacks[key].Add(new CallbackListener()
{
keepAliveObject = keepAliveObject,
callback = callback,
sendInitialState = sendInitialState
});
if (sendInitialState)
{
string val = GetRoomData(key);
if (val != null)
{
try
{
callback(val);
}
catch (Exception e)
{
Debug.LogError(e);
}
}
}
}
public static string GetDeviceData(string key)
{
return instance != null ? instance.lastState?.device?.TryGetData(key) : null;
}
public static string GetRoomData(string key)
{
return instance != null ? instance.lastState?.room?.TryGetData(key) : null;
}
/// <summary> /// <summary>
/// Sets data on the device keys themselves /// Sets data on the device keys themselves
/// </summary> /// </summary>
public static void SetDeviceBaseData(Dictionary<string, object> data) public static void SetDeviceBaseData(Dictionary<string, object> data)
{ {
data["modified_by"] = DeviceId;
instance.PostRequestCallback( instance.PostRequestCallback(
instance.velConnectUrl + "/api/v2/device/set_data/" + DeviceId, instance.velConnectUrl + "/api/v2/device/set_data/" + DeviceId,
JsonConvert.SerializeObject(data) JsonConvert.SerializeObject(data),
new Dictionary<string, string> { { "modified_by", DeviceId } }
); );
} }
@ -259,11 +479,8 @@ namespace VELConnect
{ {
instance.PostRequestCallback( instance.PostRequestCallback(
instance.velConnectUrl + "/api/v2/device/set_data/" + DeviceId, instance.velConnectUrl + "/api/v2/device/set_data/" + DeviceId,
JsonConvert.SerializeObject(new Dictionary<string, object> JsonConvert.SerializeObject(new Dictionary<string, object> { { "data", data } }),
{ new Dictionary<string, string> { { "modified_by", DeviceId } }
{ "modified_by", DeviceId },
{ "data", data }
})
); );
} }
@ -275,10 +492,10 @@ namespace VELConnect
return; return;
} }
data["modified_by"] = DeviceId;
instance.PostRequestCallback( instance.PostRequestCallback(
instance.velConnectUrl + "/api/v2/set_data/" + Application.productName + "_" + VelNetManager.Room, instance.velConnectUrl + "/api/v2/set_data/" + Application.productName + "_" + VelNetManager.Room,
JsonConvert.SerializeObject(data) JsonConvert.SerializeObject(data),
new Dictionary<string, string> { { "modified_by", DeviceId } }
); );
} }
@ -308,13 +525,13 @@ namespace VELConnect
} }
} }
public void PostRequestCallback(string url, string postData, Action<string> successCallback = null, public void PostRequestCallback(string url, string postData, Dictionary<string, string> headers = null, Action<string> successCallback = null,
Action<string> failureCallback = null) Action<string> failureCallback = null)
{ {
StartCoroutine(PostRequestCallbackCo(url, postData, successCallback, failureCallback)); StartCoroutine(PostRequestCallbackCo(url, postData, headers, successCallback, failureCallback));
} }
private static IEnumerator PostRequestCallbackCo(string url, string postData, Action<string> successCallback = null, private static IEnumerator PostRequestCallbackCo(string url, string postData, Dictionary<string, string> headers = null, Action<string> successCallback = null,
Action<string> failureCallback = null) Action<string> failureCallback = null)
{ {
UnityWebRequest webRequest = new UnityWebRequest(url, "POST"); UnityWebRequest webRequest = new UnityWebRequest(url, "POST");
@ -322,6 +539,14 @@ namespace VELConnect
UploadHandlerRaw uploadHandler = new UploadHandlerRaw(bodyRaw); UploadHandlerRaw uploadHandler = new UploadHandlerRaw(bodyRaw);
webRequest.uploadHandler = uploadHandler; webRequest.uploadHandler = uploadHandler;
webRequest.SetRequestHeader("Content-Type", "application/json"); webRequest.SetRequestHeader("Content-Type", "application/json");
if (headers != null)
{
foreach (KeyValuePair<string, string> keyValuePair in headers)
{
webRequest.SetRequestHeader(keyValuePair.Key, keyValuePair.Value);
}
}
yield return webRequest.SendWebRequest(); yield return webRequest.SendWebRequest();
switch (webRequest.result) switch (webRequest.result)

View File

@ -24,6 +24,7 @@ class DB:
return conn, curr return conn, curr
def query(self, query_string: str, data: dict = None) -> list: def query(self, query_string: str, data: dict = None) -> list:
conn = None
try: try:
conn, curr = self.create_or_connect() conn, curr = self.create_or_connect()
if data is not None: if data is not None:
@ -35,7 +36,8 @@ class DB:
return values return values
except: except:
print(traceback.print_exc()) print(traceback.print_exc())
conn.close() if conn is not None:
conn.close()
raise raise
def insert(self, query_string: str, data: dict = None) -> bool: def insert(self, query_string: str, data: dict = None) -> bool:

View File

@ -64,12 +64,12 @@ def get_device_by_pairing_code(pairing_code: str):
def create_device(hw_id: str): def create_device(hw_id: str):
db.insert(""" db.insert("""
INSERT IGNORE INTO `Device`(hw_id) VALUES (:hw_id); INSERT OR IGNORE INTO `Device`(hw_id) VALUES (:hw_id);
""", {'hw_id': hw_id}) """, {'hw_id': hw_id})
@router.get('/device/get_data/{hw_id}') @router.get('/device/get_data/{hw_id}')
def get_state(hw_id: str): def get_state(request: fastapi.Request, hw_id: str):
"""Gets the device state""" """Gets the device state"""
devices = db.query(""" devices = db.query("""
@ -77,26 +77,35 @@ def get_state(hw_id: str):
""", {'hw_id': hw_id}) """, {'hw_id': hw_id})
if len(devices) == 0: if len(devices) == 0:
return {'error': "Can't find device with that id."} return {'error': "Can't find device with that id."}
block = dict(devices[0])
if 'data' in block and block['data'] is not None:
block['data'] = json.loads(block['data'])
room_data = get_data(f"{devices[0]['current_app']}_{devices[0]['current_room']}") room_key: str = f"{devices[0]['current_app']}_{devices[0]['current_room']}"
room_data = get_data(room_key)
return {'device': devices[0], 'room': room_data} if "error" in room_data:
set_data(request, data={}, key=room_key, modified_by=None, category="room")
room_data = get_data(room_key)
return {'device': block, 'room': room_data}
@router.post('/device/set_data/{hw_id}') @router.post('/device/set_data/{hw_id}')
def set_state(hw_id: str, data: dict, request: fastapi.Request): def set_state(request: fastapi.Request, hw_id: str, data: dict, modified_by: str = None):
"""Sets the device state""" """Sets the device state"""
create_device(hw_id) create_device(hw_id)
# add the client's IP address if no sender specified # add the client's IP address if no sender specified
if 'modified_by' not in data: if 'modified_by' in data:
data['modified_by'] = str(request.client) + "_" + str(request.headers) modified_by = data['modified_by']
if modified_by is None:
modified_by: str = str(request.client) + "_" + str(request.headers)
allowed_keys: list[str] = [ allowed_keys: list[str] = [
'os_info', 'os_info',
'friendly_name', 'friendly_name',
'modified_by',
'current_app', 'current_app',
'current_room', 'current_room',
'pairing_code', 'pairing_code',
@ -107,15 +116,17 @@ def set_state(hw_id: str, data: dict, request: fastapi.Request):
db.insert(f""" db.insert(f"""
UPDATE `Device` UPDATE `Device`
SET {key}=:value, SET {key}=:value,
last_modified=CURRENT_TIMESTAMP last_modified=CURRENT_TIMESTAMP,
modified_by=:modified_by
WHERE `hw_id`=:hw_id; WHERE `hw_id`=:hw_id;
""", """,
{ {
'value': data[key], 'value': data[key],
'hw_id': hw_id, 'hw_id': hw_id,
'sender_id': data['sender_id'] 'modified_by': modified_by
}) })
if key == "data": if key == "data":
new_data = data['data']
# get the old json values and merge the data # get the old json values and merge the data
old_data_query = db.query(""" old_data_query = db.query("""
SELECT data SELECT data
@ -124,8 +135,10 @@ def set_state(hw_id: str, data: dict, request: fastapi.Request):
""", {"hw_id": hw_id}) """, {"hw_id": hw_id})
if len(old_data_query) == 1: if len(old_data_query) == 1:
old_data: dict = json.loads(old_data_query[0]["data"]) old_data: dict = {}
data = {**old_data, **data} if old_data_query[0]['data'] is not None:
old_data = json.loads(old_data_query[0]["data"])
new_data = {**old_data, **new_data}
# add the data to the db # add the data to the db
db.insert(""" db.insert("""
@ -133,7 +146,7 @@ def set_state(hw_id: str, data: dict, request: fastapi.Request):
SET data=:data, SET data=:data,
last_modified=CURRENT_TIMESTAMP last_modified=CURRENT_TIMESTAMP
WHERE hw_id=:hw_id; WHERE hw_id=:hw_id;
""", {"hw_id": hw_id, "data": json.dumps(data)}) """, {"hw_id": hw_id, "data": json.dumps(new_data)})
return {'success': True} return {'success': True}
@ -143,18 +156,22 @@ def generate_id(length: int = 4) -> str:
@router.post('/set_data') @router.post('/set_data')
def store_data_with_random_key(request: fastapi.Request, data: dict, category: str = None) -> dict: def set_data_with_random_key(request: fastapi.Request, data: dict, modified_by: str = None,
category: str = None) -> dict:
"""Creates a little storage bucket for arbitrary data with a random key""" """Creates a little storage bucket for arbitrary data with a random key"""
return store_data(request, data, None, category) return set_data(request, data, None, modified_by, category)
@router.post('/set_data/{key}') @router.post('/set_data/{key}')
def store_data(request: fastapi.Request, data: dict, key: str = None, modified_by: str = None, category: str = None) -> dict: def set_data(request: fastapi.Request, data: dict, key: str = None, modified_by: str = None,
category: str = None) -> dict:
"""Creates a little storage bucket for arbitrary data""" """Creates a little storage bucket for arbitrary data"""
# add the client's IP address if no sender specified # add the client's IP address if no sender specified
if 'modified_by' in data:
modified_by = data['modified_by']
if modified_by is None: if modified_by is None:
modified_by = str(request.client) + "_" + str(request.headers) modified_by: str = str(request.client) + "_" + str(request.headers)
# generates a key if none was supplied # generates a key if none was supplied
if key is None: if key is None:
@ -189,7 +206,7 @@ def get_data(key: str) -> dict:
"""Gets data from a storage bucket for arbitrary data""" """Gets data from a storage bucket for arbitrary data"""
data = db.query(""" data = db.query("""
SELECT data SELECT *
FROM `DataBlock` FROM `DataBlock`
WHERE id=:id WHERE id=:id
""", {"id": key}) """, {"id": key})
@ -202,7 +219,11 @@ def get_data(key: str) -> dict:
try: try:
if len(data) == 1: if len(data) == 1:
return json.loads(data[0]) block = dict(data[0])
if 'data' in block and block['data'] is not None:
block['data'] = json.loads(block['data'])
return block
return {'error': 'Not found'} return {'error': 'Not found'}
except: except Exception as e:
print(e)
return {'error': 'Unknown. Maybe no data at this key.'} return {'error': 'Unknown. Maybe no data at this key.'}

View File

@ -18,8 +18,8 @@ function httpGetAsync(theUrl, callback, failCallback) {
function httpPostAsync(theUrl, data, callback, failCallback) { function httpPostAsync(theUrl, data, callback, failCallback) {
var xmlHttp = new XMLHttpRequest(); var xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = function () { xmlHttp.onreadystatechange = function () {
if (xmlHttp.readyState == 4) { if (xmlHttp.readyState === 4) {
if (xmlHttp.status == 200) { if (xmlHttp.status === 200) {
callback(xmlHttp.responseText); callback(xmlHttp.responseText);
} else { } else {
failCallback(xmlHttp.status); failCallback(xmlHttp.status);

View File

@ -94,7 +94,7 @@
<div class="tile tile-centered col-6"> <div class="tile tile-centered col-6">
<div class="tile-content"> <div class="tile-content">
<div class="tile-title text-bold">Last Login</div> <div class="tile-title text-bold">Last Login</div>
<div class="tile-subtitle last_used">---</div> <div class="tile-subtitle last_modified">---</div>
</div> </div>
</div> </div>
</div> </div>
@ -167,6 +167,8 @@
<div class="tile-content"> <div class="tile-content">
<div class="tile-title text-bold">Current Room</div> <div class="tile-title text-bold">Current Room</div>
<input class="btn current_room" type="text" id="current_room" placeholder="----"> <input class="btn current_room" type="text" id="current_room" placeholder="----">
<input style="display: none;" class="btn current_app" type="text" id="current_app"
placeholder="----">
<button class="btn btn-primary btn-lg tooltip tooltip-right" id="set_room_id" <button class="btn btn-primary btn-lg tooltip tooltip-right" id="set_room_id"
data-tooltip="Set Room ID">Set data-tooltip="Set Room ID">Set
</button> </button>
@ -185,14 +187,36 @@
</div> </div>
</div> </div>
<br> <br>
<div class="divider text-center" data-content="Screen Sharing"></div>
<div> <div>
<p>To share your screen, download <a href="https://github.com/velaboratory/VEL-Share/releases/latest" target="_blank">VEL-Share <div>
<div class="flex flex-col h-screen relative">
<header class="flex h-16 justify-center items-center text-xl bg-black text-white">
<div class="columns">
<button id="bnt_pubcam" class="btn btn-secondary btn-lg col-6"
onclick="start(true)">Publish Camera
</button>
<button id="bnt_pubscreen" class="btn btn-secondary btn-lg col-6"
onclick="start(false)">Publish Screen
</button>
</div>
</header>
<video style="width: 100%;" id="pub_video" class="bg-black" controls></video>
</div>
<script src="https://unpkg.com/ion-sdk-js@1.5.5/dist/ion-sdk.min.js"></script>
<script src="https://unpkg.com/ion-sdk-js@1.5.5/dist/json-rpc.min.js"></script>
</div>
<p>For more screen-sharing options and remote control, download <a
href="https://github.com/velaboratory/VEL-Share/releases/latest" target="_blank">VEL-Share
<svg style="width:1em;height:1em" viewBox="0 0 24 24"> <svg style="width:1em;height:1em" viewBox="0 0 24 24">
<path fill="currentColor" <path fill="currentColor"
d="M14,3V5H17.59L7.76,14.83L9.17,16.24L19,6.41V10H21V3M19,19H5V5H12V3H5C3.89,3 3,3.9 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V12H19V19Z"/> d="M14,3V5H17.59L7.76,14.83L9.17,16.24L19,6.41V10H21V3M19,19H5V5H12V3H5C3.89,3 3,3.9 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V12H19V19Z"/>
</svg> </svg>
</a></p> </a></p>
</div> </div>
<br> <br>
</div> </div>
</div> </div>
@ -208,6 +232,7 @@
let headset_details = document.getElementById('headset_details'); let headset_details = document.getElementById('headset_details');
let hw_id_field = document.getElementById('hw_id'); let hw_id_field = document.getElementById('hw_id');
let failure = document.getElementById('failure'); let failure = document.getElementById('failure');
let current_app = document.getElementById('current_app');
let current_room = document.getElementById('current_room'); let current_room = document.getElementById('current_room');
let set_room_id = document.getElementById('set_room_id'); let set_room_id = document.getElementById('set_room_id');
let set_user_color = document.getElementById('set_user_color'); let set_user_color = document.getElementById('set_user_color');
@ -221,9 +246,10 @@
// check cookie // check cookie
let hw_id = getCookie('hw_id'); let hw_id = getCookie('hw_id');
if (hw_id !== "" && hw_id !== undefined && hw_id !== "undefined") { if (hw_id !== "" && hw_id !== undefined && hw_id !== "undefined") {
httpGetAsync('/api/get_state/' + hw_id, (resp) => { httpGetAsync('/api/v2/device/get_data/' + hw_id, (resp) => {
console.log(resp); console.log(resp);
let respData = JSON.parse(resp); let respData = JSON.parse(resp);
@ -231,20 +257,17 @@
window.location.href = "/pair"; window.location.href = "/pair";
} }
writeClass('hw_id', respData['user']['hw_id']); writeClass('hw_id', respData['device']['hw_id']);
writeClass('pairing_code', respData['user']['pairing_code']); writeClass('pairing_code', respData['device']['pairing_code']);
writeValue('current_room', respData['user']['current_room']); writeValue('current_app', respData['device']['current_app']);
writeClass('date_created', respData['user']['date_created'] + "<br>" + timeSinceString(respData['user']['date_created']) + " ago"); writeValue('current_room', respData['device']['current_room']);
writeClass('last_used', respData['user']['last_used'] + "<br>" + timeSinceString(respData['user']['last_used']) + " ago"); writeClass('date_created', respData['device']['date_created'] + "<br>" + timeSinceString(respData['device']['date_created']) + " ago");
writeValue('user_color', respData['user']['user_color']); writeClass('last_modified', respData['device']['last_modified'] + "<br>" + timeSinceString(respData['device']['last_modified']) + " ago");
if (user_color) user_color.parentElement.style.color = "" + respData['user']['user_color']; writeValue('user_name', respData['device']['friendly_name']);
writeValue('user_name', respData['user']['user_name']); writeValue('avatar_url', respData['device']['data']?.['avatar_url']);
writeValue('avatar_url', respData['user']['avatar_url']); writeValue('tv_url', respData['room']?.['data']?.['tv_url']);
if (respData['room']) { writeValue('carpet_color', respData['room']?.['data']?.['carpet_color']);
writeValue('tv_url', respData['room']['tv_url']); if (carpet_color) carpet_color.parentElement.style.color = "" + respData['room']?.['data']?.['carpet_color'];
writeValue('carpet_color', respData['room']['carpet_color']);
if (carpet_color) carpet_color.parentElement.style.color = "" + respData['room']['carpet_color'];
}
Coloris({ Coloris({
@ -271,45 +294,61 @@
failure.style.display = "block"; failure.style.display = "block";
}); });
function setUserData(data) {
data["sender_id"] = "web"; function setDeviceField(data) {
httpPostAsync('/api/set_headset_details/' + hw_id, fetch('/api/v2/device/set_data/' + hw_id, {
data, method: 'POST',
(resp) => { headers: {
console.log('success'); 'Accept': 'application/json',
'Content-Type': 'application/json'
}, },
(status) => {
console.log('fail'); body: JSON.stringify(data)
} })
); .then(_ => console.log('success'))
.catch(_ => console.log('fail'));
}
function setDeviceData(data) {
fetch('/api/v2/device/set_data/' + hw_id, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({"data": data})
})
.then(_ => console.log('success'))
.catch(_ => console.log('fail'));
} }
function setRoomData(data) { function setRoomData(data) {
data["sender_id"] = "web"; fetch('/api/v2/set_data/' + current_app.value + "_" + current_room.value, {
httpPostAsync('/api/set_room_details/' + current_room.value, method: 'POST',
data, headers: {
(resp) => { 'Accept': 'application/json',
console.log('success'); 'Content-Type': 'application/json'
}, },
(status) => { body: JSON.stringify(data)
console.log('fail'); })
} .then(_ => console.log('success'))
); .catch(_ => console.log('fail'));
} }
if (set_room_id) { if (set_room_id) {
set_room_id.addEventListener('click', () => { set_room_id.addEventListener('click', () => {
setUserData({"current_room": current_room.value}); setDeviceField({"current_room": current_room.value});
}); });
} }
if (set_user_color) { if (set_user_color) {
set_user_color.addEventListener('click', () => { set_user_color.addEventListener('click', () => {
setUserData({"user_color": document.getElementById('user_color').value}); setDeviceData({"user_color": document.getElementById('user_color').value});
}); });
} }
if (set_user_name) { if (set_user_name) {
set_user_name.addEventListener('click', () => { set_user_name.addEventListener('click', () => {
setUserData({"user_name": document.getElementById('user_name').value}); setDeviceField({"friendly_name": document.getElementById('user_name').value});
}); });
} }
if (set_tv_url) { if (set_tv_url) {
@ -324,7 +363,7 @@
} }
if (set_avatar_url) { if (set_avatar_url) {
set_avatar_url.addEventListener('click', () => { set_avatar_url.addEventListener('click', () => {
setUserData({"avatar_url": document.getElementById('avatar_url').value}); setDeviceData({"avatar_url": document.getElementById('avatar_url').value});
}); });
} }
@ -351,6 +390,84 @@
}); });
const roomName = (Math.random() + 1).toString(36).substring(7);
const pubVideo = document.getElementById("pub_video");
const subVideo = document.getElementById("sub_video");
const bntPubCam = document.getElementById("bnt_pubcam");
const bntPubScreen = document.getElementById("bnt_pubscreen");
setDeviceData({"streamer_stream_id": roomName});
const serverURL = "wss://velnet.ugavel.com/ws";
const config = {
iceServers: [
{
urls: "stun:stun.l.google.com:19302",
},
],
};
const signalLocal = new Signal.IonSFUJSONRPCSignal(serverURL);
const clientLocal = new IonSDK.Client(signalLocal, config);
signalLocal.onopen = () => clientLocal.join(roomName);
const start = (type) => {
if (type) {
IonSDK.LocalStream.getUserMedia({
resolution: "vga",
audio: true,
video: true,
codec: "vp8",
}).then((media) => {
pubVideo.srcObject = media;
pubVideo.autoplay = true;
pubVideo.controls = true;
pubVideo.muted = true;
bntPubCam.disabled = true;
bntPubScreen.disabled = true;
clientLocal.publish(media);
}).catch(console.error);
} else {
IonSDK.LocalStream.getDisplayMedia({
audio: true,
video: true,
codec: "vp8",
}).then((media) => {
pubVideo.srcObject = media;
pubVideo.autoplay = true;
pubVideo.controls = true;
pubVideo.muted = true;
bntPubCam.disabled = true;
bntPubScreen.disabled = true;
clientLocal.publish(media);
}).catch(console.error);
}
}
clientLocal.ontrack = (track, stream) => {
console.log("got track: ", track.id, "for stream: ", stream.id);
stream.mute();
stream.unmute();
if (track.kind === "video") {
subVideo.srcObject = stream;
subVideo.play();
}
//track.onunmute = () => {
//subVideo.srcObject = stream;
//subVideo.autoplay = true;
//subVideo.muted = true;
//subVideo.play();
//stream.onremovetrack = () => {
//subVideo.srcObject = null;
//}
//}
}
</script> </script>
</div> </div>
</body> </body>

View File

@ -45,48 +45,48 @@
</head> </head>
<body> <body>
<div class="container"> <div class="container">
<!-- <div class="hero bg-gray"> <!-- <div class="hero bg-gray">
<div class="hero-body"> <div class="hero-body">
<h1>Pair your headset.</h1> <h1>Pair your headset.</h1>
</div> </div>
</div> --> </div> -->
<div class="card"> <div class="card">
<div class="card-image"> <div class="card-image">
<img src="/static/img/pair_code_screenshot.png" class="img-responsive"> <img src="/static/img/pair_code_screenshot.png" class="img-responsive">
</div> </div>
<div class="card-header"> <div class="card-header">
<div class="card-title h5">Enter Pairing Code</div> <div class="card-title h5">Enter Pairing Code</div>
<div class="card-subtitle text-gray"></div> <div class="card-subtitle text-gray"></div>
</div> </div>
<div class="card-body"> <div class="card-body">
You can find the code in the bottom left of your menu tablet in conVRged. You can find the code in the bottom left of your menu tablet in conVRged.
</div> </div>
<div class="card-footer centered"> <div class="card-footer centered">
<input class="btn" type="text" id="pair_code" placeholder="0000"> <input class="btn" type="text" id="pair_code" placeholder="0000">
<button class="btn btn-primary" id="submit_pairing_code">Submit</button> <button class="btn btn-primary" id="submit_pairing_code">Submit</button>
</div>
</div> </div>
</div> </div>
</div>
<script> <script>
let submit_button = document.getElementById('submit_pairing_code'); let submit_button = document.getElementById('submit_pairing_code');
let pair_code_input = document.getElementById('pair_code'); let pair_code_input = document.getElementById('pair_code');
submit_button.addEventListener('click', () => { submit_button.addEventListener('click', () => {
httpGetAsync('/api/pair_headset/' + pair_code_input.value, (resp) => { httpGetAsync('/api/v2/get_device_by_pairing_code/' + pair_code_input.value, (resp) => {
console.log(resp); console.log(resp);
let respData = JSON.parse(resp); let respData = JSON.parse(resp);
if (respData['hw_id'] != '') { if (respData['hw_id'] !== '') {
setCookie('hw_id', respData['hw_id'], 60); setCookie('hw_id', respData['hw_id'], 60);
window.location.href = "/"; window.location.href = "/";
} }
}, (status) => { }, (status) => {
window.location.href = "/failure"; window.location.href = "/failure";
});
}); });
</script> });
</script>
</body> </body>
</html> </html>