added device actions
This commit is contained in:
parent
cd986c4f56
commit
aa3301ac35
10 changed files with 557 additions and 15 deletions
149
src/pbHelpers/Device.ts
Normal file
149
src/pbHelpers/Device.ts
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
import { Device, Tag } from "models";
|
||||
import moment from "moment";
|
||||
import { filterByTags } from "pbHelpers/Tag";
|
||||
import { GetDeviceProductLabel } from "products/DeviceProduct";
|
||||
import { pond } from "protobuf-ts/pond";
|
||||
import { quack } from "protobuf-ts/quack";
|
||||
import { capitalize } from "utils/strings";
|
||||
import { getDevicePlatformLabel } from "./DevicePlatform";
|
||||
import { filterByDeviceState } from "./DeviceState";
|
||||
import { filterByPower } from "./Power";
|
||||
import { getUpgradeChannelLabel } from "./UpgradeChannel";
|
||||
|
||||
//complex filter algorithm for searching a list of devices
|
||||
export function filterDevices(searchValue: string, devices: Device[], tags: Tag[]): Device[] {
|
||||
return devices.filter(device =>
|
||||
filterDevice(searchValue.toLowerCase(), device, deviceTags(device, tags))
|
||||
);
|
||||
}
|
||||
|
||||
//helper function for filterDevices
|
||||
export function filterDevice(searchValue: string, device: Device, tags: Tag[]): boolean {
|
||||
const idMatch: boolean = filterByDeviceID(searchValue, device.settings.deviceId.toString());
|
||||
const nameMatch: boolean = filterByDeviceName(searchValue, device.name());
|
||||
const tagsMatch: boolean = filterByTags(searchValue, tags);
|
||||
const deviceStateMatch: boolean = filterByDeviceState(searchValue, device.status.state);
|
||||
const powerMatch: boolean = filterByPower(
|
||||
searchValue,
|
||||
device.status.power ? pond.DevicePower.create(device.status.power) : undefined
|
||||
);
|
||||
const lastActiveMatch: boolean = filterByLastActive(searchValue, device.status.lastActive);
|
||||
return idMatch || nameMatch || tagsMatch || powerMatch || deviceStateMatch || lastActiveMatch;
|
||||
}
|
||||
|
||||
export function filterByDeviceID(searchValue: string, deviceIDString: string): boolean {
|
||||
return searchValue.replace("id:", "").trim() === deviceIDString;
|
||||
}
|
||||
|
||||
export function filterByDeviceName(searchValue: string, deviceName: string): boolean {
|
||||
return (
|
||||
deviceName.toLowerCase().indexOf(
|
||||
searchValue
|
||||
.replace("name:", "")
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
) > -1
|
||||
);
|
||||
}
|
||||
|
||||
export function filterByDeviceDescription(searchValue: string, deviceDescription: string): boolean {
|
||||
return (
|
||||
deviceDescription.toLowerCase().indexOf(
|
||||
searchValue
|
||||
.replace("description:", "")
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
) > -1
|
||||
);
|
||||
}
|
||||
|
||||
export function filterByLastActive(searchValue: string, lastActive: string): boolean {
|
||||
return (
|
||||
moment(lastActive)
|
||||
.fromNow()
|
||||
.indexOf(searchValue.replace("last active:", "")) > -1
|
||||
);
|
||||
}
|
||||
|
||||
export function deviceTags(device: Device, tags: Tag[]): Tag[] {
|
||||
return tags.filter(tag => device.status.tagKeys.some(key => key === tag.settings.key));
|
||||
}
|
||||
|
||||
const keyTranslator = new Map<keyof pond.DeviceSettings, string>([
|
||||
["deviceId", "Device ID"],
|
||||
["name", "Name"],
|
||||
["description", "Description"],
|
||||
["sleepType", "Sleep Type"],
|
||||
["sleepDurationS", "Sleep Duration"],
|
||||
["sleepDelayMs", "Sleep Delay"],
|
||||
["pondCheckPeriodS", "Check Period"],
|
||||
["removed", "Removed"],
|
||||
["platform", "Platform"],
|
||||
["wakePin", "Wake Pin"],
|
||||
["platform", "Platform"],
|
||||
["automaticallyUpgrade", "Upgrades Automatically"],
|
||||
["product", "Product"],
|
||||
["upgradeChannel", "Upgrade Channel"],
|
||||
["latitude", "Latitude"],
|
||||
["longitude", "Longitude"],
|
||||
["theme", "Marker Theme"],
|
||||
["extensionComponents", "Extension Components"],
|
||||
["mutations", "Device Mutations"]
|
||||
]);
|
||||
|
||||
// Keys will be stringified by default if not found in the keyTranslator
|
||||
export function TranslateKey(key: keyof pond.DeviceSettings): string {
|
||||
let translatedKey = keyTranslator.get(key);
|
||||
return translatedKey ? translatedKey : capitalize(key.toString());
|
||||
}
|
||||
|
||||
const valueTranslator = new Map<keyof pond.DeviceSettings, (device: pond.DeviceSettings) => string>(
|
||||
[
|
||||
["name", device => device.name],
|
||||
["description", device => device.description],
|
||||
[
|
||||
"sleepType",
|
||||
device => {
|
||||
switch (device.sleepType) {
|
||||
case quack.SleepType.SLEEP_TYPE_DEEP:
|
||||
return "Deep Sleep";
|
||||
case quack.SleepType.SLEEP_TYPE_HIBERNATE:
|
||||
return "Hibernate";
|
||||
case quack.SleepType.SLEEP_TYPE_NAP:
|
||||
return "Nap";
|
||||
default:
|
||||
return "None";
|
||||
}
|
||||
}
|
||||
],
|
||||
["sleepDurationS", device => device.sleepDurationS.toString() + "s"],
|
||||
["sleepDelayMs", device => (device.sleepDelayMs / 1000).toString() + "s"],
|
||||
["pondCheckPeriodS", device => device.pondCheckPeriodS.toString() + "s"],
|
||||
["platform", device => getDevicePlatformLabel(device.platform)],
|
||||
["product", device => GetDeviceProductLabel(device.product)],
|
||||
["upgradeChannel", device => getUpgradeChannelLabel(device.upgradeChannel)],
|
||||
["automaticallyUpgrade", device => device.automaticallyUpgrade.toString()],
|
||||
["latitude", device => device.latitude.toString()],
|
||||
["longitude", device => device.longitude.toString()],
|
||||
["theme", device => "Colour: " + device.theme?.color + "Size: " + device.theme?.height],
|
||||
["extensionComponents", device => device.extensionComponents.toString()],
|
||||
["mutations", device => device.mutations.toString()]
|
||||
]
|
||||
);
|
||||
|
||||
// Values will be stringified by default if its key is not found in the valueTranslator
|
||||
export function TranslateValue(key: keyof pond.DeviceSettings, device: pond.DeviceSettings) {
|
||||
//console.log(device)
|
||||
//console.log(key)
|
||||
let translatorFunc = valueTranslator.get(key);
|
||||
let dKey = device[key];
|
||||
return translatorFunc ? translatorFunc(device) : capitalize(dKey ? dKey.toString() : "");
|
||||
}
|
||||
|
||||
//checks the user's path to determine if they are on a shared url
|
||||
export function isShareableLink(rawDeviceID: string): boolean {
|
||||
if (!rawDeviceID) {
|
||||
return false;
|
||||
}
|
||||
return isNaN(Number(rawDeviceID));
|
||||
}
|
||||
128
src/pbHelpers/Power.tsx
Normal file
128
src/pbHelpers/Power.tsx
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
import {
|
||||
Battery20,
|
||||
Battery30,
|
||||
Battery50,
|
||||
Battery60,
|
||||
Battery80,
|
||||
Battery90,
|
||||
BatteryAlert,
|
||||
BatteryCharging20,
|
||||
BatteryCharging30,
|
||||
BatteryCharging50,
|
||||
BatteryCharging60,
|
||||
BatteryCharging80,
|
||||
BatteryCharging90,
|
||||
BatteryChargingFull,
|
||||
BatteryFull,
|
||||
BatteryUnknown,
|
||||
PowerOff,
|
||||
Power
|
||||
} from "@mui/icons-material";
|
||||
import { pond } from "protobuf-ts/pond";
|
||||
import { quack } from "protobuf-ts/quack";
|
||||
|
||||
export interface PowerDescriber {
|
||||
description: string;
|
||||
icon: any;
|
||||
}
|
||||
|
||||
interface batteryThreshold {
|
||||
threshold: number;
|
||||
icon: any;
|
||||
}
|
||||
|
||||
let chargingThresholds: batteryThreshold[] = [
|
||||
{ threshold: 100, icon: <BatteryChargingFull style={{ color: "var(--status-ok)" }} /> },
|
||||
{ threshold: 90, icon: <BatteryCharging90 style={{ color: "var(--status-ok)" }} /> },
|
||||
{ threshold: 80, icon: <BatteryCharging80 style={{ color: "var(--status-ok)" }} /> },
|
||||
{ threshold: 60, icon: <BatteryCharging60 style={{ color: "var(--status-unstable)" }} /> },
|
||||
{ threshold: 40, icon: <BatteryCharging50 style={{ color: "var(--status-unstable)" }} /> },
|
||||
{ threshold: 30, icon: <BatteryCharging30 style={{ color: "var(--status-warning)" }} /> },
|
||||
{ threshold: 20, icon: <BatteryCharging20 style={{ color: "var(--status-warning)" }} /> },
|
||||
{ threshold: 1, icon: <BatteryCharging20 style={{ color: "var(--status-alert)" }} /> },
|
||||
{ threshold: 0, icon: <BatteryAlert style={{ color: "var(--status-alert)" }} /> }
|
||||
];
|
||||
|
||||
let notChargingThresholds: batteryThreshold[] = [
|
||||
{ threshold: 100, icon: <BatteryFull style={{ color: "var(--status-ok)" }} /> },
|
||||
{ threshold: 90, icon: <Battery90 style={{ color: "var(--status-ok)" }} /> },
|
||||
{ threshold: 80, icon: <Battery80 style={{ color: "var(--status-ok)" }} /> },
|
||||
{ threshold: 60, icon: <Battery60 style={{ color: "var(--status-unstable)" }} /> },
|
||||
{ threshold: 40, icon: <Battery50 style={{ color: "var(--status-unstable)" }} /> },
|
||||
{ threshold: 30, icon: <Battery30 style={{ color: "var(--status-warning)" }} /> },
|
||||
{ threshold: 20, icon: <Battery20 style={{ color: "var(--status-warning)" }} /> },
|
||||
{ threshold: 1, icon: <Battery20 style={{ color: "var(--status-alert)" }} /> },
|
||||
{ threshold: 0, icon: <BatteryAlert style={{ color: "var(--status-alert)" }} /> }
|
||||
];
|
||||
|
||||
export function describePower(power?: pond.DevicePower | null): PowerDescriber {
|
||||
let result = {
|
||||
description: "",
|
||||
icon: <BatteryUnknown style={{ color: "var(--status-unknown)" }} />
|
||||
};
|
||||
if (!power) return result;
|
||||
if (power.type === quack.PowerSubtype.POWER_SUBTYPE_NO_BATTERY) {
|
||||
if (power.inputVoltage > 0) {
|
||||
result.icon = <Power style={{ color: "var(--status-ok)" }} />;
|
||||
result.description = "Plugged in";
|
||||
} else {
|
||||
result.icon = <PowerOff style={{ color: "var(--status-alert)" }} />;
|
||||
result.description = "Unplugged";
|
||||
}
|
||||
} else {
|
||||
let thresholds = notChargingThresholds;
|
||||
let suffix = "not charging";
|
||||
if (power.inputVoltage >= 4) {
|
||||
thresholds = chargingThresholds;
|
||||
if (power.chargePercent === 100) {
|
||||
suffix = "charged";
|
||||
} else {
|
||||
suffix = "charging";
|
||||
}
|
||||
}
|
||||
result.description = power.chargePercent.toString() + "%, " + suffix;
|
||||
for (let i = 0; i < thresholds.length; i++) {
|
||||
if (power.chargePercent >= thresholds[i].threshold) {
|
||||
result.icon = thresholds[i].icon;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//returns a boolean indicating whether the search term matches a power query
|
||||
export function filterByPower(searchValue: string, power: pond.DevicePower | undefined): boolean {
|
||||
let matches: string[] = [];
|
||||
|
||||
if (power) {
|
||||
switch (power.type) {
|
||||
case quack.PowerSubtype.POWER_SUBTYPE_NO_BATTERY:
|
||||
if (power.inputVoltage > 0) {
|
||||
matches.push("on");
|
||||
} else {
|
||||
matches.push("off");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
const isCharging = power.inputVoltage >= 4.0;
|
||||
if (isCharging) {
|
||||
matches.push("charging");
|
||||
}
|
||||
|
||||
if (power.chargePercent >= 0 && power.chargePercent <= 33) {
|
||||
matches.push("low");
|
||||
} else if (power.chargePercent <= 66) {
|
||||
matches.push("medium");
|
||||
} else {
|
||||
matches.push("high");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
matches = ["unknown", "?"];
|
||||
}
|
||||
|
||||
return matches.some(match => match.indexOf(searchValue.replace("power:", "").trim()) > -1);
|
||||
}
|
||||
19
src/pbHelpers/Tag.tsx
Normal file
19
src/pbHelpers/Tag.tsx
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import { Tag } from "models";
|
||||
|
||||
export function filterByTags(searchValue: string, tags: Tag[]): boolean {
|
||||
return tags.some(tag => filterByTag(searchValue, tag));
|
||||
}
|
||||
|
||||
export function filterByTag(searchValue: string, tag: Tag): boolean {
|
||||
return (
|
||||
tag
|
||||
.name()
|
||||
.toLowerCase()
|
||||
.indexOf(
|
||||
searchValue
|
||||
.replace("tag:", "")
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
) > -1
|
||||
);
|
||||
}
|
||||
54
src/pbHelpers/UpgradeChannel.ts
Normal file
54
src/pbHelpers/UpgradeChannel.ts
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
import { pond } from "protobuf-ts/pond";
|
||||
|
||||
export interface UpgradeChannelHelper {
|
||||
label: string;
|
||||
colour: string;
|
||||
}
|
||||
|
||||
const Unknown: UpgradeChannelHelper = {
|
||||
label: "Unknown Channel",
|
||||
colour: "var(--status-ok)"
|
||||
};
|
||||
|
||||
const Alpha: UpgradeChannelHelper = {
|
||||
label: "Alpha",
|
||||
colour: "var(--status-risk)"
|
||||
};
|
||||
|
||||
const Beta: UpgradeChannelHelper = {
|
||||
label: "Beta",
|
||||
colour: "var(--status-unstable)"
|
||||
};
|
||||
|
||||
const Stable: UpgradeChannelHelper = {
|
||||
label: "Stable",
|
||||
colour: "var(--status-ok)"
|
||||
};
|
||||
|
||||
const Development: UpgradeChannelHelper = {
|
||||
label: "Development",
|
||||
colour: "var(--status-warning)"
|
||||
};
|
||||
|
||||
export function getUpgradeChannelHelper(channel: pond.UpgradeChannel): UpgradeChannelHelper {
|
||||
switch (channel) {
|
||||
case pond.UpgradeChannel.UPGRADE_CHANNEL_ALPHA:
|
||||
return Alpha;
|
||||
case pond.UpgradeChannel.UPGRADE_CHANNEL_BETA:
|
||||
return Beta;
|
||||
case pond.UpgradeChannel.UPGRADE_CHANNEL_STABLE:
|
||||
return Stable;
|
||||
case pond.UpgradeChannel.UPGRADE_CHANNEL_DEVELOPMENT:
|
||||
return Development;
|
||||
default:
|
||||
return Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
export function getUpgradeChannelLabel(channel: pond.UpgradeChannel): string {
|
||||
return getUpgradeChannelHelper(channel).label;
|
||||
}
|
||||
|
||||
export function getUpgradeChannelColour(channel: pond.UpgradeChannel): string {
|
||||
return getUpgradeChannelHelper(channel).colour;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue