mostly getting device and rooms correctly
parent
c6670adec0
commit
2555c3082f
|
|
@ -8,6 +8,8 @@
|
||||||
"name": "example-dashboard",
|
"name": "example-dashboard",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"humanize-duration": "^3.28.0",
|
||||||
|
"luxon": "^3.3.0",
|
||||||
"pocketbase": "^0.15.2"
|
"pocketbase": "^0.15.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
@ -1802,6 +1804,11 @@
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/humanize-duration": {
|
||||||
|
"version": "3.28.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/humanize-duration/-/humanize-duration-3.28.0.tgz",
|
||||||
|
"integrity": "sha512-jMAxraOOmHuPbffLVDKkEKi/NeG8dMqP8lGRd6Tbf7JgAeG33jjgPWDbXXU7ypCI0o+oNKJFgbSB9FKVdWNI2A=="
|
||||||
|
},
|
||||||
"node_modules/ignore": {
|
"node_modules/ignore": {
|
||||||
"version": "5.2.4",
|
"version": "5.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
|
||||||
|
|
@ -2034,6 +2041,14 @@
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/luxon": {
|
||||||
|
"version": "3.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/luxon/-/luxon-3.3.0.tgz",
|
||||||
|
"integrity": "sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/magic-string": {
|
"node_modules/magic-string": {
|
||||||
"version": "0.30.1",
|
"version": "0.30.1",
|
||||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.1.tgz",
|
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.1.tgz",
|
||||||
|
|
@ -4383,6 +4398,11 @@
|
||||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"humanize-duration": {
|
||||||
|
"version": "3.28.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/humanize-duration/-/humanize-duration-3.28.0.tgz",
|
||||||
|
"integrity": "sha512-jMAxraOOmHuPbffLVDKkEKi/NeG8dMqP8lGRd6Tbf7JgAeG33jjgPWDbXXU7ypCI0o+oNKJFgbSB9FKVdWNI2A=="
|
||||||
|
},
|
||||||
"ignore": {
|
"ignore": {
|
||||||
"version": "5.2.4",
|
"version": "5.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
|
||||||
|
|
@ -4563,6 +4583,11 @@
|
||||||
"yallist": "^4.0.0"
|
"yallist": "^4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"luxon": {
|
||||||
|
"version": "3.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/luxon/-/luxon-3.3.0.tgz",
|
||||||
|
"integrity": "sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg=="
|
||||||
|
},
|
||||||
"magic-string": {
|
"magic-string": {
|
||||||
"version": "0.30.1",
|
"version": "0.30.1",
|
||||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.1.tgz",
|
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.1.tgz",
|
||||||
|
|
|
||||||
|
|
@ -21,16 +21,18 @@
|
||||||
"eslint-plugin-svelte": "^2.30.0",
|
"eslint-plugin-svelte": "^2.30.0",
|
||||||
"prettier": "^2.8.0",
|
"prettier": "^2.8.0",
|
||||||
"prettier-plugin-svelte": "^2.10.1",
|
"prettier-plugin-svelte": "^2.10.1",
|
||||||
|
"sass": "^1.63.6",
|
||||||
"svelte": "^4.0.0",
|
"svelte": "^4.0.0",
|
||||||
"svelte-check": "^3.4.3",
|
"svelte-check": "^3.4.3",
|
||||||
"svelte-preprocess": "^5.0.4",
|
"svelte-preprocess": "^5.0.4",
|
||||||
"tslib": "^2.4.1",
|
"tslib": "^2.4.1",
|
||||||
"typescript": "^5.0.0",
|
"typescript": "^5.0.0",
|
||||||
"vite": "^4.3.6",
|
"vite": "^4.3.6"
|
||||||
"sass": "^1.63.6"
|
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"humanize-duration": "^3.28.0",
|
||||||
|
"luxon": "^3.3.0",
|
||||||
"pocketbase": "^0.15.2"
|
"pocketbase": "^0.15.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
<link rel="icon" href="%sveltekit.assets%/favicons/favicon.ico" />
|
||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width" />
|
||||||
%sveltekit.head%
|
%sveltekit.head%
|
||||||
</head>
|
</head>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { currentUser, pb } from '../velconnect';
|
import { currentUser, pb } from '$lib/js/velconnect';
|
||||||
|
|
||||||
let email: string;
|
let email: string;
|
||||||
let password: string;
|
let password: string;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { currentDevice, currentUser, pb, type Device, pairedDevices } from '../velconnect';
|
import { currentDevice, currentUser, pb, type DeviceData, pairedDevices } from '$lib/js/velconnect';
|
||||||
|
|
||||||
let pairingCode: string;
|
let pairingCode: string;
|
||||||
let errorMessage: string | null;
|
let errorMessage: string | null;
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
try {
|
try {
|
||||||
let device = (await pb
|
let device = (await pb
|
||||||
.collection('Device')
|
.collection('Device')
|
||||||
.getFirstListItem(`pairing_code="${pairingCode}"`)) as Device;
|
.getFirstListItem(`pairing_code="${pairingCode}"`)) as DeviceData;
|
||||||
|
|
||||||
// add it to the local data
|
// add it to the local data
|
||||||
currentDevice.set(device.id);
|
currentDevice.set(device.id);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
import { DateTime } from 'luxon';
|
||||||
|
import humanizeDuration from 'humanize-duration';
|
||||||
|
|
||||||
|
export function prettyDate(date: string | Date | DateTime, includeYear = false) {
|
||||||
|
if (date == null) return '';
|
||||||
|
|
||||||
|
let d: DateTime;
|
||||||
|
if (date instanceof Date) {
|
||||||
|
d = DateTime.fromJSDate(date);
|
||||||
|
} else if (date instanceof DateTime) {
|
||||||
|
d = date;
|
||||||
|
} else {
|
||||||
|
date = date.replace(' ', 'T');
|
||||||
|
d = DateTime.fromISO(date);
|
||||||
|
}
|
||||||
|
// return DateTime.fromISO(date).toFormat("yyyy-LL-dd hh:mm a ZZZZ");
|
||||||
|
const fromNow = DateTime.utc().minus(d.toMillis()).toMillis();
|
||||||
|
const yearReplace = includeYear ? '' : ', 2023';
|
||||||
|
if (fromNow > 0) {
|
||||||
|
return `${d.toLocaleString(DateTime.DATETIME_MED)} (${humanizeDuration(fromNow, {
|
||||||
|
round: true,
|
||||||
|
largest: 1
|
||||||
|
})} ago)`.replaceAll(yearReplace, '');
|
||||||
|
} else {
|
||||||
|
return `${d.toLocaleString(DateTime.DATETIME_MED)} (in ${humanizeDuration(fromNow, {
|
||||||
|
round: true,
|
||||||
|
largest: 1
|
||||||
|
})})`.replaceAll(yearReplace, '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -18,7 +18,7 @@ export const currentDevice = writable('');
|
||||||
interface HasData extends Record {
|
interface HasData extends Record {
|
||||||
data: { [key: string]: string };
|
data: { [key: string]: string };
|
||||||
}
|
}
|
||||||
export interface Device extends Record {
|
export interface DeviceData extends Record {
|
||||||
current_room: string;
|
current_room: string;
|
||||||
current_app: string;
|
current_app: string;
|
||||||
data: { [key: string]: string };
|
data: { [key: string]: string };
|
||||||
|
|
@ -2,23 +2,26 @@
|
||||||
import { onDestroy, onMount } from 'svelte';
|
import { onDestroy, onMount } from 'svelte';
|
||||||
import {
|
import {
|
||||||
currentDevice,
|
currentDevice,
|
||||||
type Device,
|
type DeviceData,
|
||||||
pairedDevices,
|
pairedDevices,
|
||||||
pb,
|
pb,
|
||||||
type RoomData,
|
type RoomData,
|
||||||
currentUser
|
currentUser
|
||||||
} from '../lib/velconnect';
|
} from '$lib/js/velconnect';
|
||||||
import Login from '$lib/components/Login.svelte';
|
import Login from '$lib/components/Login.svelte';
|
||||||
import Pair from '$lib/components/Pair.svelte';
|
import Pair from '$lib/components/Pair.svelte';
|
||||||
|
import { prettyDate } from '$lib/js/util';
|
||||||
|
|
||||||
if ($currentDevice == '' && $pairedDevices.length > 0) {
|
if ($currentDevice == '' && $pairedDevices.length > 0) {
|
||||||
currentDevice.set($pairedDevices[0]);
|
currentDevice.set($pairedDevices[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
let unsubscribeDeviceData: () => void;
|
let unsubscribeDeviceData: () => void;
|
||||||
|
let unsubscribeRoomData: () => void;
|
||||||
let unsubscribeCurrentDevice: () => void;
|
let unsubscribeCurrentDevice: () => void;
|
||||||
|
let unsubscribeCurrentUser: () => void;
|
||||||
|
|
||||||
let deviceData: Device | null;
|
let deviceData: DeviceData | null;
|
||||||
let roomData: RoomData | null;
|
let roomData: RoomData | null;
|
||||||
|
|
||||||
let sending = false;
|
let sending = false;
|
||||||
|
|
@ -38,21 +41,50 @@
|
||||||
unsubscribeDeviceData?.();
|
unsubscribeDeviceData?.();
|
||||||
if (val != '') {
|
if (val != '') {
|
||||||
deviceData = await pb.collection('Device').getOne($currentDevice);
|
deviceData = await pb.collection('Device').getOne($currentDevice);
|
||||||
unsubscribeDeviceData = await pb.collection('Device').subscribe(val, (data) => {
|
if (deviceData != null) getRoomData(deviceData);
|
||||||
deviceData = data.record as Device;
|
unsubscribeDeviceData = await pb.collection('Device').subscribe(val, async (data) => {
|
||||||
|
deviceData = data.record as DeviceData;
|
||||||
|
getRoomData(deviceData);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
deviceData = null;
|
deviceData = null;
|
||||||
roomData = null;
|
roomData = null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
unsubscribeCurrentUser = currentUser.subscribe((user) => {
|
||||||
|
pairedDevices.set(user?.devices ?? []);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
unsubscribeCurrentDevice?.();
|
unsubscribeCurrentDevice?.();
|
||||||
unsubscribeDeviceData?.();
|
unsubscribeDeviceData?.();
|
||||||
|
unsubscribeRoomData?.();
|
||||||
|
unsubscribeCurrentUser?.();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
async function getRoomData(deviceData: DeviceData) {
|
||||||
|
// get room data
|
||||||
|
unsubscribeRoomData?.();
|
||||||
|
// create or just fetch room by name
|
||||||
|
roomData = await fetch(
|
||||||
|
`${pb.baseUrl}/data_block/${deviceData.current_app}_${deviceData.current_room}`,
|
||||||
|
{
|
||||||
|
method: 'POST'
|
||||||
|
}
|
||||||
|
).then((r) => r.json());
|
||||||
|
console.log(roomData);
|
||||||
|
if (roomData) {
|
||||||
|
unsubscribeDeviceData = await pb.collection('DataBlock').subscribe(roomData.id, (data) => {
|
||||||
|
roomData = data.record as RoomData;
|
||||||
|
unsubscribeRoomData?.();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.error('Failed to get or create room');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let abortController = new AbortController();
|
let abortController = new AbortController();
|
||||||
function delayedSend() {
|
function delayedSend() {
|
||||||
console.log('fn: delayedSend()');
|
console.log('fn: delayedSend()');
|
||||||
|
|
@ -106,6 +138,10 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>VEL-Connect</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
<h1>VEL-Connect</h1>
|
<h1>VEL-Connect</h1>
|
||||||
<img src="/img/velconnect_logo_1.png" alt="logo" width="70px" height="28px" />
|
<img src="/img/velconnect_logo_1.png" alt="logo" width="70px" height="28px" />
|
||||||
<p>
|
<p>
|
||||||
|
|
@ -119,20 +155,22 @@
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h3>Devices:</h3>
|
<h3>Devices:</h3>
|
||||||
{#each $pairedDevices as d}
|
<div class="device-list">
|
||||||
<div>
|
{#each $pairedDevices as d}
|
||||||
<button
|
<div>
|
||||||
on:click={() => {
|
<button
|
||||||
currentDevice.set(d);
|
on:click={() => {
|
||||||
}}>{d}</button
|
currentDevice.set(d);
|
||||||
>
|
}}>{d}</button
|
||||||
<button
|
>
|
||||||
on:click={() => {
|
<button
|
||||||
removeDevice(d);
|
on:click={() => {
|
||||||
}}>x</button
|
removeDevice(d);
|
||||||
>
|
}}>x</button
|
||||||
</div>
|
>
|
||||||
{/each}
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
{#if $pairedDevices.length == 0}
|
{#if $pairedDevices.length == 0}
|
||||||
<p>No devices paired. Enter a pairing code above.</p>
|
<p>No devices paired. Enter a pairing code above.</p>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
@ -158,11 +196,11 @@
|
||||||
</device-field>
|
</device-field>
|
||||||
<device-field>
|
<device-field>
|
||||||
<h6>First Seen</h6>
|
<h6>First Seen</h6>
|
||||||
<p>{deviceData.created}</p>
|
<p>{prettyDate(deviceData.created)}</p>
|
||||||
</device-field>
|
</device-field>
|
||||||
<device-field>
|
<device-field>
|
||||||
<h6>Last Seen</h6>
|
<h6>Last Seen</h6>
|
||||||
<p>{deviceData.updated}</p>
|
<p>{prettyDate(deviceData.updated)}</p>
|
||||||
</device-field>
|
</device-field>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -218,8 +256,11 @@
|
||||||
</device-field>
|
</device-field>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h6>Raw JSON:</h6>
|
<h3>Raw JSON:</h3>
|
||||||
|
<h6>Device Data</h6>
|
||||||
<pre><code>{JSON.stringify(deviceData, null, 2)}</code></pre>
|
<pre><code>{JSON.stringify(deviceData, null, 2)}</code></pre>
|
||||||
|
<h6>Room Data</h6>
|
||||||
|
<pre><code>{JSON.stringify(roomData, null, 2)}</code></pre>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|
@ -232,4 +273,19 @@
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.device-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: fit-content;
|
||||||
|
gap: 0.5em;
|
||||||
|
|
||||||
|
& > div {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.2em;
|
||||||
|
& > button:first-child {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.5 KiB |
|
|
@ -4,6 +4,7 @@ using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
|
|
@ -35,17 +36,17 @@ namespace VELConnect
|
||||||
|
|
||||||
public class Device
|
public class Device
|
||||||
{
|
{
|
||||||
public readonly string id;
|
[CanBeNull] public readonly string id;
|
||||||
public readonly DateTime created;
|
[CanBeNull] public string created = null;
|
||||||
public readonly DateTime updated;
|
[CanBeNull] public string updated = null;
|
||||||
public string device_id;
|
[CanBeNull] public string device_id;
|
||||||
public string os_info;
|
[CanBeNull] public string os_info;
|
||||||
public string friendly_name;
|
[CanBeNull] public string friendly_name;
|
||||||
public string modified_by;
|
[CanBeNull] public string modified_by;
|
||||||
public string current_app;
|
[CanBeNull] public string current_app;
|
||||||
public string current_room;
|
[CanBeNull] public string current_room;
|
||||||
public string pairing_code;
|
[CanBeNull] public string pairing_code;
|
||||||
public DateTime last_online;
|
[CanBeNull] public string last_online;
|
||||||
public Dictionary<string, string> data;
|
public Dictionary<string, string> data;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -88,9 +89,9 @@ namespace VELConnect
|
||||||
|
|
||||||
public class UserCount
|
public class UserCount
|
||||||
{
|
{
|
||||||
public readonly string id;
|
[CanBeNull] public readonly string id;
|
||||||
public readonly DateTime created;
|
public readonly DateTime? created;
|
||||||
public readonly DateTime updated;
|
public readonly DateTime? updated;
|
||||||
public string device_id;
|
public string device_id;
|
||||||
public string app_id;
|
public string app_id;
|
||||||
public string room_id;
|
public string room_id;
|
||||||
|
|
@ -100,7 +101,20 @@ namespace VELConnect
|
||||||
public string platform;
|
public string platform;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum DeviceField
|
||||||
|
{
|
||||||
|
device_id,
|
||||||
|
os_info,
|
||||||
|
friendly_name,
|
||||||
|
modified_by,
|
||||||
|
current_app,
|
||||||
|
current_room,
|
||||||
|
pairing_code,
|
||||||
|
last_online
|
||||||
|
}
|
||||||
|
|
||||||
public State lastState;
|
public State lastState;
|
||||||
|
public State state;
|
||||||
|
|
||||||
public static Action<State> OnInitialState;
|
public static Action<State> OnInitialState;
|
||||||
public static Action<string, string> OnDeviceFieldChanged;
|
public static Action<string, string> OnDeviceFieldChanged;
|
||||||
|
|
@ -136,7 +150,7 @@ namespace VELConnect
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
Hash128 hash = new Hash128();
|
Hash128 hash = new Hash128();
|
||||||
hash.Append(DeviceId);
|
hash.Append(deviceId);
|
||||||
// change once a day
|
// change once a day
|
||||||
hash.Append(DateTime.UtcNow.DayOfYear);
|
hash.Append(DateTime.UtcNow.DayOfYear);
|
||||||
// between 1000 and 9999 inclusive (any 4 digit number)
|
// between 1000 and 9999 inclusive (any 4 digit number)
|
||||||
|
|
@ -144,52 +158,52 @@ namespace VELConnect
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string DeviceId
|
private static string deviceId;
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
// allows running multiple builds on the same computer
|
|
||||||
// return SystemInfo.deviceUniqueIdentifier + Hash128.Compute(Application.dataPath);
|
|
||||||
return SystemInfo.deviceUniqueIdentifier + "_EDITOR";
|
|
||||||
#else
|
|
||||||
return SystemInfo.deviceUniqueIdentifier;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Awake()
|
private void Awake()
|
||||||
{
|
{
|
||||||
if (_instance != null) Debug.LogError("VELConnectManager instance already exists", this);
|
if (_instance != null) Debug.LogError("VELConnectManager instance already exists", this);
|
||||||
_instance = this;
|
_instance = this;
|
||||||
|
|
||||||
|
// Compute device id
|
||||||
|
MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
|
||||||
|
StringBuilder sb = new StringBuilder(SystemInfo.deviceUniqueIdentifier);
|
||||||
|
sb.Append(Application.productName);
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
// allows running multiple builds on the same computer
|
||||||
|
// return SystemInfo.deviceUniqueIdentifier + Hash128.Compute(Application.dataPath);
|
||||||
|
sb.Append(Application.dataPath);
|
||||||
|
sb.Append("EDITOR");
|
||||||
|
#endif
|
||||||
|
string id = Convert.ToBase64String(md5.ComputeHash(Encoding.UTF8.GetBytes(sb.ToString())));
|
||||||
|
deviceId = id[..15];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start is called before the first frame update
|
// Start is called before the first frame update
|
||||||
private void Start()
|
private void Start()
|
||||||
{
|
{
|
||||||
SetDeviceField(new State.Device
|
SetDeviceField(new Dictionary<DeviceField, string>
|
||||||
{
|
{
|
||||||
os_info = SystemInfo.operatingSystem,
|
{ DeviceField.os_info, SystemInfo.operatingSystem },
|
||||||
friendly_name = SystemInfo.deviceName,
|
{ DeviceField.friendly_name, SystemInfo.deviceName },
|
||||||
current_app = Application.productName,
|
{ DeviceField.current_app, Application.productName },
|
||||||
pairing_code = PairingCode,
|
{ DeviceField.pairing_code, PairingCode },
|
||||||
});
|
});
|
||||||
|
|
||||||
UpdateUserCount();
|
// UpdateUserCount();
|
||||||
|
|
||||||
StartCoroutine(SlowLoop());
|
StartCoroutine(SlowLoop());
|
||||||
|
|
||||||
VelNetManager.OnJoinedRoom += room =>
|
VelNetManager.OnJoinedRoom += room =>
|
||||||
{
|
{
|
||||||
SetDeviceField(new State.Device
|
SetDeviceField(new Dictionary<DeviceField, string>
|
||||||
{
|
{
|
||||||
current_app = Application.productName,
|
{ DeviceField.current_app, Application.productName },
|
||||||
current_room = room,
|
{ DeviceField.current_room, room },
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void UpdateUserCount(bool leaving = false)
|
private void UpdateUserCount(bool leaving = false)
|
||||||
{
|
{
|
||||||
if (!VelNetManager.InRoom) return;
|
if (!VelNetManager.InRoom) return;
|
||||||
|
|
@ -198,7 +212,7 @@ namespace VELConnect
|
||||||
{
|
{
|
||||||
UserCount postData = new UserCount
|
UserCount postData = new UserCount
|
||||||
{
|
{
|
||||||
device_id = DeviceId,
|
device_id = deviceId,
|
||||||
app_id = Application.productName,
|
app_id = Application.productName,
|
||||||
room_id = VelNetManager.Room ?? "",
|
room_id = VelNetManager.Room ?? "",
|
||||||
total_users = rooms.rooms.Sum(r => r.numUsers) - (leaving ? 1 : 0),
|
total_users = rooms.rooms.Sum(r => r.numUsers) - (leaving ? 1 : 0),
|
||||||
|
|
@ -219,9 +233,9 @@ namespace VELConnect
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
GetRequestCallback(velConnectUrl + "/state/device/" + DeviceId, json =>
|
GetRequestCallback(velConnectUrl + "/state/device/" + deviceId, json =>
|
||||||
{
|
{
|
||||||
State state = JsonConvert.DeserializeObject<State>(json);
|
state = JsonConvert.DeserializeObject<State>(json);
|
||||||
if (state == null) return;
|
if (state == null) return;
|
||||||
|
|
||||||
bool isInitialState = false;
|
bool isInitialState = false;
|
||||||
|
|
@ -380,6 +394,10 @@ namespace VELConnect
|
||||||
}
|
}
|
||||||
|
|
||||||
lastState = state;
|
lastState = state;
|
||||||
|
if (lastState?.device?.pairing_code == null)
|
||||||
|
{
|
||||||
|
Debug.LogError("Pairing code nulllll");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
|
@ -510,33 +528,46 @@ namespace VELConnect
|
||||||
return _instance != null ? _instance.lastState?.room?.TryGetData(key) : null;
|
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
|
||||||
/// These are fixed fields defined for every application
|
/// These are fixed fields defined for every application
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static void SetDeviceField(State.Device device)
|
public static void SetDeviceField(Dictionary<DeviceField, string> device)
|
||||||
{
|
{
|
||||||
device.last_online = DateTime.UtcNow;
|
device[DeviceField.last_online] = DateTime.UtcNow.ToLongDateString();
|
||||||
|
|
||||||
// update our local state, so we don't get change events on our own updates
|
if (_instance.state?.device != null)
|
||||||
if (_instance.lastState?.device != null)
|
|
||||||
{
|
{
|
||||||
FieldInfo[] fields = device.GetType().GetFields();
|
|
||||||
|
|
||||||
// loop through all the fields in the device
|
// loop through all the fields in the device
|
||||||
foreach (FieldInfo fieldInfo in fields)
|
foreach (DeviceField key in device.Keys.ToArray())
|
||||||
{
|
{
|
||||||
fieldInfo.SetValue(_instance.lastState.device, fieldInfo.GetValue(device));
|
FieldInfo field = _instance.state.device.GetType().GetField(key.ToString());
|
||||||
|
if ((string)field.GetValue(_instance.state.device) != device[key])
|
||||||
|
{
|
||||||
|
if (_instance.lastState?.device != null)
|
||||||
|
{
|
||||||
|
// update our local state, so we don't get change events on our own updates
|
||||||
|
field.SetValue(_instance.lastState.device, device[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// don't send this field, since it's the same
|
||||||
|
device.Remove(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// last_online field always changes
|
||||||
|
if (device.Keys.Count <= 1)
|
||||||
|
{
|
||||||
|
// nothing changed, don't send
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PostRequestCallback(
|
PostRequestCallback(
|
||||||
_instance.velConnectUrl + "/device/" + DeviceId,
|
_instance.velConnectUrl + "/device/" + deviceId,
|
||||||
JsonConvert.SerializeObject(device, Formatting.None, new JsonSerializerSettings
|
JsonConvert.SerializeObject(device)
|
||||||
{
|
|
||||||
NullValueHandling = NullValueHandling.Ignore
|
|
||||||
})
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -545,23 +576,44 @@ namespace VELConnect
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static void SetDeviceData(Dictionary<string, string> data)
|
public static void SetDeviceData(Dictionary<string, string> data)
|
||||||
{
|
{
|
||||||
State.Device device = new State.Device
|
if (_instance.state?.device != null)
|
||||||
{
|
{
|
||||||
last_online = DateTime.UtcNow,
|
foreach (string key in data.Keys.ToList())
|
||||||
data = data,
|
|
||||||
};
|
|
||||||
|
|
||||||
// update our local state, so we don't get change events on our own updates
|
|
||||||
if (_instance.lastState?.device != null)
|
|
||||||
{
|
|
||||||
foreach (KeyValuePair<string, string> kvp in data)
|
|
||||||
{
|
{
|
||||||
_instance.lastState.device.data[kvp.Key] = kvp.Value;
|
// if the value is unchanged from the current state, remove it so we don't double-update
|
||||||
|
if (_instance.state.device.data.TryGetValue(key, out string val) && val == data[key])
|
||||||
|
{
|
||||||
|
data.Remove(key);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// update our local state, so we don't get change events on our own updates
|
||||||
|
if (_instance.lastState?.device?.data != null)
|
||||||
|
{
|
||||||
|
_instance.lastState.device.data[key] = data[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nothing was changed
|
||||||
|
if (data.Keys.Count == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we have no data, just set the whole thing
|
||||||
|
if (_instance.lastState?.device != null) _instance.lastState.device.data ??= data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Dictionary<string, object> device = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "last_online", DateTime.UtcNow.ToLongDateString() },
|
||||||
|
{ "data", data },
|
||||||
|
};
|
||||||
|
|
||||||
PostRequestCallback(
|
PostRequestCallback(
|
||||||
_instance.velConnectUrl + "/device/" + DeviceId,
|
_instance.velConnectUrl + "/device/" + deviceId,
|
||||||
JsonConvert.SerializeObject(device, Formatting.None, new JsonSerializerSettings
|
JsonConvert.SerializeObject(device, Formatting.None, new JsonSerializerSettings
|
||||||
{
|
{
|
||||||
NullValueHandling = NullValueHandling.Ignore
|
NullValueHandling = NullValueHandling.Ignore
|
||||||
|
|
@ -589,6 +641,24 @@ namespace VELConnect
|
||||||
data = data
|
data = data
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// remove keys that already match our current state
|
||||||
|
if (_instance.state?.room != null)
|
||||||
|
{
|
||||||
|
foreach (string key in data.Keys.ToArray())
|
||||||
|
{
|
||||||
|
if (_instance.state.room.data[key] == data[key])
|
||||||
|
{
|
||||||
|
data.Remove(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we have no changed values
|
||||||
|
if (data.Keys.Count == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// update our local state, so we don't get change events on our own updates
|
// update our local state, so we don't get change events on our own updates
|
||||||
if (_instance.lastState?.room != null)
|
if (_instance.lastState?.room != null)
|
||||||
{
|
{
|
||||||
|
|
@ -727,7 +797,7 @@ namespace VELConnect
|
||||||
|
|
||||||
private void OnApplicationFocus(bool focus)
|
private void OnApplicationFocus(bool focus)
|
||||||
{
|
{
|
||||||
UpdateUserCount(!focus);
|
// UpdateUserCount(!focus);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/pocketbase/dbx"
|
||||||
|
"github.com/pocketbase/pocketbase/daos"
|
||||||
|
m "github.com/pocketbase/pocketbase/migrations"
|
||||||
|
"github.com/pocketbase/pocketbase/models/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
m.Register(func(db dbx.Builder) error {
|
||||||
|
dao := daos.New(db);
|
||||||
|
|
||||||
|
collection, err := dao.FindCollectionByNameOrId("fupstz47c55s69f")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// add
|
||||||
|
new_current_room_id := &schema.SchemaField{}
|
||||||
|
json.Unmarshal([]byte(`{
|
||||||
|
"system": false,
|
||||||
|
"id": "wvpaovjo",
|
||||||
|
"name": "current_room_id",
|
||||||
|
"type": "relation",
|
||||||
|
"required": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"collectionId": "3qwwkz4wb0lyi78",
|
||||||
|
"cascadeDelete": false,
|
||||||
|
"minSelect": null,
|
||||||
|
"maxSelect": 1,
|
||||||
|
"displayFields": []
|
||||||
|
}
|
||||||
|
}`), new_current_room_id)
|
||||||
|
collection.Schema.AddField(new_current_room_id)
|
||||||
|
|
||||||
|
return dao.SaveCollection(collection)
|
||||||
|
}, func(db dbx.Builder) error {
|
||||||
|
dao := daos.New(db);
|
||||||
|
|
||||||
|
collection, err := dao.FindCollectionByNameOrId("fupstz47c55s69f")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove
|
||||||
|
collection.Schema.RemoveField("wvpaovjo")
|
||||||
|
|
||||||
|
return dao.SaveCollection(collection)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/pocketbase/dbx"
|
||||||
|
"github.com/pocketbase/pocketbase/daos"
|
||||||
|
m "github.com/pocketbase/pocketbase/migrations"
|
||||||
|
"github.com/pocketbase/pocketbase/models/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
m.Register(func(db dbx.Builder) error {
|
||||||
|
dao := daos.New(db);
|
||||||
|
|
||||||
|
collection, err := dao.FindCollectionByNameOrId("fupstz47c55s69f")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove
|
||||||
|
collection.Schema.RemoveField("wvpaovjo")
|
||||||
|
|
||||||
|
return dao.SaveCollection(collection)
|
||||||
|
}, func(db dbx.Builder) error {
|
||||||
|
dao := daos.New(db);
|
||||||
|
|
||||||
|
collection, err := dao.FindCollectionByNameOrId("fupstz47c55s69f")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// add
|
||||||
|
del_current_room_id := &schema.SchemaField{}
|
||||||
|
json.Unmarshal([]byte(`{
|
||||||
|
"system": false,
|
||||||
|
"id": "wvpaovjo",
|
||||||
|
"name": "current_room_id",
|
||||||
|
"type": "relation",
|
||||||
|
"required": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"collectionId": "3qwwkz4wb0lyi78",
|
||||||
|
"cascadeDelete": false,
|
||||||
|
"minSelect": null,
|
||||||
|
"maxSelect": 1,
|
||||||
|
"displayFields": []
|
||||||
|
}
|
||||||
|
}`), del_current_room_id)
|
||||||
|
collection.Schema.AddField(del_current_room_id)
|
||||||
|
|
||||||
|
return dao.SaveCollection(collection)
|
||||||
|
})
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue