mirror of
https://github.com/meshtastic/meshtastic.git
synced 2024-11-09 23:24:10 -08:00
Add oem tool
This commit is contained in:
parent
6ccb434b4e
commit
5f997f5bc8
|
@ -21,7 +21,9 @@
|
|||
"@heroicons/react": "^2.0.12",
|
||||
"@leenguyen/react-flip-clock-countdown": "^1.3.1",
|
||||
"@mdx-js/react": "^1.6.22",
|
||||
"@meshtastic/meshtasticjs": "^0.6.113",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"base64-js": "^1.5.1",
|
||||
"dotenv": "^16.0.3",
|
||||
"framer-motion": "^6.5.1",
|
||||
"postcss": "^8.4.18",
|
||||
|
|
180
pnpm-lock.yaml
180
pnpm-lock.yaml
|
@ -11,12 +11,14 @@ specifiers:
|
|||
'@leenguyen/react-flip-clock-countdown': ^1.3.1
|
||||
'@mdx-js/react': ^1.6.22
|
||||
'@meshtastic/eslint-config': ^1.0.8
|
||||
'@meshtastic/meshtasticjs': ^0.6.113
|
||||
'@tailwindcss/typography': ^0.5.7
|
||||
'@tsconfig/docusaurus': ^1.0.6
|
||||
'@types/node': ^18.11.9
|
||||
'@types/react': ^18.0.24
|
||||
'@types/react-dom': ^18.0.8
|
||||
autoprefixer: ^10.4.13
|
||||
base64-js: ^1.5.1
|
||||
dotenv: ^16.0.3
|
||||
framer-motion: ^6.5.1
|
||||
postcss: ^8.4.18
|
||||
|
@ -40,7 +42,9 @@ dependencies:
|
|||
'@heroicons/react': 2.0.12_react@17.0.2
|
||||
'@leenguyen/react-flip-clock-countdown': 1.3.1_react@17.0.2
|
||||
'@mdx-js/react': 1.6.22_react@17.0.2
|
||||
'@meshtastic/meshtasticjs': 0.6.113
|
||||
autoprefixer: 10.4.13_postcss@8.4.18
|
||||
base64-js: 1.5.1
|
||||
dotenv: 16.0.3
|
||||
framer-motion: 6.5.1_sfoxds7t5ydpegc3knd667wn6m
|
||||
postcss: 8.4.18
|
||||
|
@ -2652,6 +2656,18 @@ packages:
|
|||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@meshtastic/meshtasticjs/0.6.113:
|
||||
resolution: {integrity: sha512-4/Qlqof/Dm2gWS1nqZ/H/csYy5o7EjdKlGWCBFQjAA5s9PmZ4vMI20UdoSaAz3+DvW7CYCzAx3h1e5WwsClF0A==}
|
||||
dependencies:
|
||||
'@protobuf-ts/runtime': 2.8.1
|
||||
'@serialport/stream': 10.3.0
|
||||
glob: 8.0.3
|
||||
serialport: 10.4.0
|
||||
sub-events: 1.9.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/@motionone/animation/10.13.1:
|
||||
resolution: {integrity: sha512-dxQ+1wWxL6iFHDy1uv6hhcPjIdOg36eDT56jN4LI7Z5HZRyLpq8x1t7JFQclo/IEIb+6Bk4atmyinGFdXVECuA==}
|
||||
dependencies:
|
||||
|
@ -2721,6 +2737,106 @@ packages:
|
|||
resolution: {integrity: sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==}
|
||||
dev: false
|
||||
|
||||
/@protobuf-ts/runtime/2.8.1:
|
||||
resolution: {integrity: sha512-D9M5hSumYCovIfNllt7N6ODh4q+LrjiMWtNETvooaf+a2XheZJ7kgjFlsFghti0CFWwtA//of4JXQfw9hU+cCw==}
|
||||
dev: false
|
||||
|
||||
/@serialport/binding-mock/10.2.2:
|
||||
resolution: {integrity: sha512-HAFzGhk9OuFMpuor7aT5G1ChPgn5qSsklTFOTUX72Rl6p0xwcSVsRtG/xaGp6bxpN7fI9D/S8THLBWbBgS6ldw==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
dependencies:
|
||||
'@serialport/bindings-interface': 1.2.2
|
||||
debug: 4.3.4
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/@serialport/bindings-cpp/10.7.0:
|
||||
resolution: {integrity: sha512-Xx1wA2UCG2loS32hxNvWJI4smCzGKhWqE85//fLRzHoGgE1lSLe3Nk7W40/ebrlGFHWRbQZmeaIF4chb2XLliA==}
|
||||
engines: {node: '>=12.17.0 <13.0 || >=14.0.0'}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
'@serialport/bindings-interface': 1.2.1
|
||||
'@serialport/parser-readline': 10.3.0
|
||||
debug: 4.3.4
|
||||
node-addon-api: 4.3.0
|
||||
node-gyp-build: 4.5.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/@serialport/bindings-interface/1.2.1:
|
||||
resolution: {integrity: sha512-63Dyqz2gtryRDDckFusOYqLYhR3Hq/M4sEdbF9i/VsvDb6T+tNVgoAKUZ+FMrXXKnCSu+hYbk+MTc0XQANszxw==}
|
||||
engines: {node: ^12.22 || ^14.13 || >=16}
|
||||
dev: false
|
||||
|
||||
/@serialport/bindings-interface/1.2.2:
|
||||
resolution: {integrity: sha512-CJaUd5bLvtM9c5dmO9rPBHPXTa9R2UwpkJ0wdh9JCYcbrPWsKz+ErvR0hBLeo7NPeiFdjFO4sonRljiw4d2XiA==}
|
||||
engines: {node: ^12.22 || ^14.13 || >=16}
|
||||
dev: false
|
||||
|
||||
/@serialport/parser-byte-length/10.3.0:
|
||||
resolution: {integrity: sha512-pJ/VoFemzKRRNDHLhFfPThwP40QrGaEnm9TtwL7o2GihEPwzBg3T0bN13ew5TpbbUYZdMpUtpm3CGfl6av9rUQ==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
dev: false
|
||||
|
||||
/@serialport/parser-cctalk/10.3.0:
|
||||
resolution: {integrity: sha512-8ujmk8EvVbDPrNF4mM33bWvUYJOZ0wXbY3WCRazHRWvyCdL0VO0DQvW81ZqgoTpiDQZm5r8wQu9rmuemahF6vQ==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
dev: false
|
||||
|
||||
/@serialport/parser-delimiter/10.3.0:
|
||||
resolution: {integrity: sha512-9E4Vj6s0UbbcCCTclwegHGPYjJhdm9qLCS0lowXQDEQC5naZnbsELemMHs93nD9jHPcyx1B4oXkMnVZLxX5TYw==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
dev: false
|
||||
|
||||
/@serialport/parser-inter-byte-timeout/10.3.0:
|
||||
resolution: {integrity: sha512-wKP0QK85NHgvT6BBB1qBfKBBU4pf8kespNXAZBUYmFT+P4n8r8IZE2mqigCD+AiZcfWNQoAizwOsT/Jx/qeVig==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
dev: false
|
||||
|
||||
/@serialport/parser-packet-length/10.3.0:
|
||||
resolution: {integrity: sha512-bj0cWzt8YSQj/E5fRQVYdi4TsfTlZQrXlXrUwjyTsCONv8IPOHzsz+yY0fw5SEMiJtaLyqvPkCHLsttOd/zFsg==}
|
||||
engines: {node: '>=8.6.0'}
|
||||
dev: false
|
||||
|
||||
/@serialport/parser-readline/10.3.0:
|
||||
resolution: {integrity: sha512-ki3ATZ3/RAqnqGROBKE7k+OeZ0DZXZ53GTca4q71OU5RazbbNhTOBQLKLXD3v9QZXCMJdg4hGW/2Y0DuMUqMQg==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
dependencies:
|
||||
'@serialport/parser-delimiter': 10.3.0
|
||||
dev: false
|
||||
|
||||
/@serialport/parser-ready/10.3.0:
|
||||
resolution: {integrity: sha512-1owywJ4p592dJyVrEJZPIh6pUZ3/y/LN6kGTDH2wxdewRUITo/sGvDy0er5i2+dJD3yuowiAz0dOHSdz8tevJA==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
dev: false
|
||||
|
||||
/@serialport/parser-regex/10.3.0:
|
||||
resolution: {integrity: sha512-tIogTs7CvTH+UUFnsvE7i33MSISyTPTGPWlglWYH2/5coipXY503jlaYS1YGe818wWNcSx6YAjMZRdhTWwM39w==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
dev: false
|
||||
|
||||
/@serialport/parser-slip-encoder/10.3.0:
|
||||
resolution: {integrity: sha512-JI0ILF5sylWn8f0MuMzHFBix/iMUTa79/Z95KaPZYnVaEdA7h7hh/o21Jmon/26P3RJwL1SNJCjZ81zfan+LtQ==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
dev: false
|
||||
|
||||
/@serialport/parser-spacepacket/10.3.0:
|
||||
resolution: {integrity: sha512-PDF73ClEPsClD1FEJZHNuBevDKsJCkqy/XD5+S5eA6+tY5D4HLrVgSWsg+3qqB6+dlpwf2CzHe+uO8D3teuKHA==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
dev: false
|
||||
|
||||
/@serialport/stream/10.3.0:
|
||||
resolution: {integrity: sha512-7sooi5fHogYNVEJwxVdg872xO6TuMgQd2E9iRmv+o8pk/1dbBnPkmH6Ka3st1mVE+0KnIJqVlgei+ncSsqXIGw==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
dependencies:
|
||||
'@serialport/bindings-interface': 1.2.1
|
||||
debug: 4.3.4
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/@sideway/address/4.1.4:
|
||||
resolution: {integrity: sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==}
|
||||
dependencies:
|
||||
|
@ -3703,6 +3819,10 @@ packages:
|
|||
resolution: {integrity: sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ==}
|
||||
dev: false
|
||||
|
||||
/base64-js/1.5.1:
|
||||
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
||||
dev: false
|
||||
|
||||
/batch/0.6.1:
|
||||
resolution: {integrity: sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=}
|
||||
dev: false
|
||||
|
@ -3780,6 +3900,12 @@ packages:
|
|||
balanced-match: 1.0.2
|
||||
concat-map: 0.0.1
|
||||
|
||||
/brace-expansion/2.0.1:
|
||||
resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
|
||||
dependencies:
|
||||
balanced-match: 1.0.2
|
||||
dev: false
|
||||
|
||||
/braces/3.0.2:
|
||||
resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==}
|
||||
engines: {node: '>=8'}
|
||||
|
@ -5505,6 +5631,17 @@ packages:
|
|||
once: 1.4.0
|
||||
path-is-absolute: 1.0.1
|
||||
|
||||
/glob/8.0.3:
|
||||
resolution: {integrity: sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
fs.realpath: 1.0.0
|
||||
inflight: 1.0.6
|
||||
inherits: 2.0.4
|
||||
minimatch: 5.1.0
|
||||
once: 1.4.0
|
||||
dev: false
|
||||
|
||||
/global-dirs/3.0.0:
|
||||
resolution: {integrity: sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==}
|
||||
engines: {node: '>=10'}
|
||||
|
@ -6642,6 +6779,13 @@ packages:
|
|||
dependencies:
|
||||
brace-expansion: 1.1.11
|
||||
|
||||
/minimatch/5.1.0:
|
||||
resolution: {integrity: sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==}
|
||||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
brace-expansion: 2.0.1
|
||||
dev: false
|
||||
|
||||
/minimist/1.2.6:
|
||||
resolution: {integrity: sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==}
|
||||
|
||||
|
@ -6691,6 +6835,10 @@ packages:
|
|||
tslib: 2.4.0
|
||||
dev: false
|
||||
|
||||
/node-addon-api/4.3.0:
|
||||
resolution: {integrity: sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==}
|
||||
dev: false
|
||||
|
||||
/node-emoji/1.11.0:
|
||||
resolution: {integrity: sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==}
|
||||
dependencies:
|
||||
|
@ -6714,6 +6862,11 @@ packages:
|
|||
engines: {node: '>= 6.13.0'}
|
||||
dev: false
|
||||
|
||||
/node-gyp-build/4.5.0:
|
||||
resolution: {integrity: sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg==}
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/node-releases/2.0.6:
|
||||
resolution: {integrity: sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==}
|
||||
|
||||
|
@ -8337,6 +8490,28 @@ packages:
|
|||
dependencies:
|
||||
randombytes: 2.1.0
|
||||
|
||||
/serialport/10.4.0:
|
||||
resolution: {integrity: sha512-PszPM5SnFMgSXom60PkKS2A9nMlNbHkuoyRBlzdSWw9rmgOn258+V0dYbWMrETJMM+TJV32vqBzjg5MmmUMwMw==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
dependencies:
|
||||
'@serialport/binding-mock': 10.2.2
|
||||
'@serialport/bindings-cpp': 10.7.0
|
||||
'@serialport/parser-byte-length': 10.3.0
|
||||
'@serialport/parser-cctalk': 10.3.0
|
||||
'@serialport/parser-delimiter': 10.3.0
|
||||
'@serialport/parser-inter-byte-timeout': 10.3.0
|
||||
'@serialport/parser-packet-length': 10.3.0
|
||||
'@serialport/parser-readline': 10.3.0
|
||||
'@serialport/parser-ready': 10.3.0
|
||||
'@serialport/parser-regex': 10.3.0
|
||||
'@serialport/parser-slip-encoder': 10.3.0
|
||||
'@serialport/parser-spacepacket': 10.3.0
|
||||
'@serialport/stream': 10.3.0
|
||||
debug: 4.3.4
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/serve-handler/6.1.3:
|
||||
resolution: {integrity: sha512-FosMqFBNrLyeiIDvP1zgO6YoTzFYHxLDEIavhlmQ+knB2Z7l1t+kGLHkZIDN7UVWqQAmKI3D20A6F6jo3nDd4w==}
|
||||
dependencies:
|
||||
|
@ -8682,6 +8857,11 @@ packages:
|
|||
postcss-selector-parser: 6.0.10
|
||||
dev: false
|
||||
|
||||
/sub-events/1.9.0:
|
||||
resolution: {integrity: sha512-dnFBayilG9Ku0k/lNs1Y7WV4kv91+ovCoeBV3uIYrY49DylvBb6z9d9ED2ctcrvX2YlReFalpCgJNtSgmrOaJg==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
dev: false
|
||||
|
||||
/supports-color/5.5.0:
|
||||
resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
|
||||
engines: {node: '>=4'}
|
||||
|
|
149
src/pages/tools/OEM.tsx
Normal file
149
src/pages/tools/OEM.tsx
Normal file
|
@ -0,0 +1,149 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import { fromByteArray, toByteArray } from 'base64-js';
|
||||
|
||||
import { Protobuf } from '@meshtastic/meshtasticjs';
|
||||
import Layout from '@theme/Layout';
|
||||
|
||||
const OEM = (): JSX.Element => {
|
||||
const [oemAesKey, setOemAesKey] = useState<Uint8Array>(new Uint8Array());
|
||||
const [oemFont, setOemFont] = useState<Protobuf.ScreenFonts>(
|
||||
Protobuf.ScreenFonts.FONT_MEDIUM,
|
||||
);
|
||||
const [oemIconBits, setOemIconBits] = useState<Uint8Array>(new Uint8Array());
|
||||
const [oemIconHeight, setOemIconHeight] = useState<number>(0);
|
||||
const [oemIconWidth, setOemIconWidth] = useState<number>(0);
|
||||
const [oemText, setOemText] = useState<string>('');
|
||||
const [oemBytes, setOemBytes] = useState<Uint8Array>(new Uint8Array());
|
||||
|
||||
const encoder = new TextEncoder();
|
||||
|
||||
useEffect(() => {
|
||||
setOemBytes(
|
||||
Protobuf.OEMStore.toBinary({
|
||||
oemAesKey,
|
||||
oemFont,
|
||||
oemIconBits,
|
||||
oemIconHeight,
|
||||
oemIconWidth,
|
||||
oemText,
|
||||
}),
|
||||
);
|
||||
}, [oemAesKey, oemFont, oemIconBits, oemIconHeight, oemIconWidth, oemText]);
|
||||
|
||||
const enumOptions = Protobuf.ScreenFonts
|
||||
? Object.entries(Protobuf.ScreenFonts).filter(
|
||||
(value) => typeof value[1] === 'number',
|
||||
)
|
||||
: [];
|
||||
|
||||
const readFile = (file: File) => {
|
||||
return new Promise((resolve: (value: ArrayBuffer) => void, reject) => {
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onload = (res) => {
|
||||
resolve(res.target.result as ArrayBuffer);
|
||||
};
|
||||
reader.onerror = (err) => reject(err);
|
||||
|
||||
reader.readAsArrayBuffer(file);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Layout title="OEM Generator" description="OEM Bin Generator">
|
||||
<div className="container mt-8 flex flex-col gap-3">
|
||||
<span>AES Key</span>
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onClick={() => {
|
||||
const key = new Uint8Array(128 / 8);
|
||||
setOemAesKey(crypto.getRandomValues(key));
|
||||
}}
|
||||
className="cursor-pointer rounded-md bg-tertiary p-2 hover:brightness-90"
|
||||
>
|
||||
Generate 128bit
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
const key = new Uint8Array(256 / 8);
|
||||
setOemAesKey(crypto.getRandomValues(key));
|
||||
}}
|
||||
className="mr-auto cursor-pointer rounded-md bg-tertiary p-2 hover:brightness-90"
|
||||
>
|
||||
Generate 256bit
|
||||
</button>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
name="oemAesKey"
|
||||
value={fromByteArray(oemAesKey)}
|
||||
onChange={(e) => {
|
||||
setOemAesKey(toByteArray(e.target.value));
|
||||
}}
|
||||
/>
|
||||
<span>Font</span>
|
||||
<select
|
||||
onChange={(e) => {
|
||||
setOemFont(parseInt(e.target.value));
|
||||
}}
|
||||
>
|
||||
{enumOptions.map(([name, value], index) => (
|
||||
<option key={index} value={value}>
|
||||
{name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<span>Logo XBM</span>
|
||||
<input
|
||||
type="file"
|
||||
name="file"
|
||||
onChange={(e) => {
|
||||
readFile(e.target.files[0]).then((data) => {
|
||||
setOemIconBits(new Uint8Array(data));
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<span>Logo Height</span>
|
||||
<input
|
||||
type="number"
|
||||
name="oemIconHeight"
|
||||
onChange={(e) => {
|
||||
setOemIconHeight(parseInt(e.target.value));
|
||||
}}
|
||||
/>
|
||||
<span>Logo Width</span>
|
||||
<input
|
||||
type="number"
|
||||
name="oemIconWidth"
|
||||
onChange={(e) => {
|
||||
setOemIconWidth(parseInt(e.target.value));
|
||||
}}
|
||||
/>
|
||||
<span>Boot Text</span>
|
||||
<input
|
||||
type="text"
|
||||
name="oemText"
|
||||
onChange={(e) => {
|
||||
setOemText(e.target.value);
|
||||
}}
|
||||
/>
|
||||
<a
|
||||
className="cursor-pointer rounded-md bg-tertiary p-2 hover:brightness-90"
|
||||
download="OEM.bin"
|
||||
onClick={() => {
|
||||
const blob = new Blob([oemBytes], {
|
||||
type: 'application/octet-stream',
|
||||
});
|
||||
window.open(URL.createObjectURL(blob));
|
||||
}}
|
||||
>
|
||||
Download
|
||||
</a>
|
||||
{oemBytes.toString()}
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
|
||||
export default OEM;
|
Loading…
Reference in a new issue