@jjlmoya/utils-hardware 1.27.0 → 1.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/category/index.ts +2 -1
- package/src/entries.ts +4 -1
- package/src/index.ts +1 -0
- package/src/tests/locale_completeness.test.ts +1 -1
- package/src/tests/tool_validation.test.ts +1 -1
- package/src/tool/mobileSensorTest/bibliography.astro +14 -0
- package/src/tool/mobileSensorTest/bibliography.ts +24 -0
- package/src/tool/mobileSensorTest/component.astro +241 -0
- package/src/tool/mobileSensorTest/entry.ts +29 -0
- package/src/tool/mobileSensorTest/i18n/de.ts +186 -0
- package/src/tool/mobileSensorTest/i18n/en.ts +186 -0
- package/src/tool/mobileSensorTest/i18n/es.ts +186 -0
- package/src/tool/mobileSensorTest/i18n/fr.ts +186 -0
- package/src/tool/mobileSensorTest/i18n/id.ts +186 -0
- package/src/tool/mobileSensorTest/i18n/it.ts +186 -0
- package/src/tool/mobileSensorTest/i18n/ja.ts +186 -0
- package/src/tool/mobileSensorTest/i18n/ko.ts +186 -0
- package/src/tool/mobileSensorTest/i18n/nl.ts +186 -0
- package/src/tool/mobileSensorTest/i18n/pl.ts +186 -0
- package/src/tool/mobileSensorTest/i18n/pt.ts +186 -0
- package/src/tool/mobileSensorTest/i18n/ru.ts +186 -0
- package/src/tool/mobileSensorTest/i18n/sv.ts +186 -0
- package/src/tool/mobileSensorTest/i18n/tr.ts +186 -0
- package/src/tool/mobileSensorTest/i18n/zh.ts +186 -0
- package/src/tool/mobileSensorTest/index.ts +11 -0
- package/src/tool/mobileSensorTest/logic.ts +39 -0
- package/src/tool/mobileSensorTest/mobile-sensor-test.css +460 -0
- package/src/tool/mobileSensorTest/seo.astro +15 -0
- package/src/tool/mobileSensorTest/ui.ts +31 -0
- package/src/tools.ts +2 -1
package/package.json
CHANGED
package/src/category/index.ts
CHANGED
|
@@ -17,10 +17,11 @@ import { webBluetoothBleScanner } from '../tool/webBluetoothBleScanner/index';
|
|
|
17
17
|
import { keyboardChatterTest } from '../tool/keyboardChatterTest/index';
|
|
18
18
|
import { webUsbSerialMonitor } from '../tool/webUsbSerialMonitor/index';
|
|
19
19
|
import { usbPowerBudgetCalculator } from '../tool/usbPowerBudgetCalculator/index';
|
|
20
|
+
import { mobileSensorTest } from '../tool/mobileSensorTest/index';
|
|
20
21
|
|
|
21
22
|
export const hardwareCategory: HardwareCategoryEntry = {
|
|
22
23
|
icon: 'mdi:memory',
|
|
23
|
-
tools: [pixelesPantalla, testTeclado, keyboardChatterTest, testMando, probadorVibracionMando, testRaton, mouseDoubleClickTest, mouseScrollTest, estimadorSaludBateria, toneGenerator, refreshRateDetector, monitorGhostingTest, spectrumCanvas, upsRuntimeCalculator, usbPowerBudgetCalculator, stereoAudioTest, webBluetoothBleScanner, webUsbSerialMonitor],
|
|
24
|
+
tools: [pixelesPantalla, testTeclado, keyboardChatterTest, testMando, probadorVibracionMando, testRaton, mouseDoubleClickTest, mouseScrollTest, estimadorSaludBateria, toneGenerator, refreshRateDetector, monitorGhostingTest, spectrumCanvas, upsRuntimeCalculator, usbPowerBudgetCalculator, mobileSensorTest, stereoAudioTest, webBluetoothBleScanner, webUsbSerialMonitor],
|
|
24
25
|
i18n: {
|
|
25
26
|
en: () => import('./i18n/en').then((m) => m.content),
|
|
26
27
|
es: () => import('./i18n/es').then((m) => m.content),
|
package/src/entries.ts
CHANGED
|
@@ -34,6 +34,8 @@ export { webUsbSerialMonitor } from './tool/webUsbSerialMonitor/entry';
|
|
|
34
34
|
export type { WebUsbSerialMonitorLocaleContent } from './tool/webUsbSerialMonitor/entry';
|
|
35
35
|
export { usbPowerBudgetCalculator } from './tool/usbPowerBudgetCalculator/entry';
|
|
36
36
|
export type { UsbPowerBudgetCalculatorLocaleContent } from './tool/usbPowerBudgetCalculator/entry';
|
|
37
|
+
export { mobileSensorTest } from './tool/mobileSensorTest/entry';
|
|
38
|
+
export type { MobileSensorTestLocaleContent } from './tool/mobileSensorTest/entry';
|
|
37
39
|
export { hardwareCategory } from './category';
|
|
38
40
|
import { estimadorSaludBateria } from './tool/batteryHealthEstimator/entry';
|
|
39
41
|
import { pixelesPantalla } from './tool/deadPixelTest/entry';
|
|
@@ -53,4 +55,5 @@ import { webBluetoothBleScanner } from './tool/webBluetoothBleScanner/entry';
|
|
|
53
55
|
import { keyboardChatterTest } from './tool/keyboardChatterTest/entry';
|
|
54
56
|
import { webUsbSerialMonitor } from './tool/webUsbSerialMonitor/entry';
|
|
55
57
|
import { usbPowerBudgetCalculator } from './tool/usbPowerBudgetCalculator/entry';
|
|
56
|
-
|
|
58
|
+
import { mobileSensorTest } from './tool/mobileSensorTest/entry';
|
|
59
|
+
export const ALL_ENTRIES = [estimadorSaludBateria, pixelesPantalla, testMando, probadorVibracionMando, testTeclado, keyboardChatterTest, testRaton, mouseDoubleClickTest, mouseScrollTest, toneGenerator, refreshRateDetector, monitorGhostingTest, spectrumCanvas, upsRuntimeCalculator, usbPowerBudgetCalculator, mobileSensorTest, stereoAudioTest, webBluetoothBleScanner, webUsbSerialMonitor];
|
package/src/index.ts
CHANGED
|
@@ -35,3 +35,4 @@ export { WEB_BLUETOOTH_BLE_SCANNER_TOOL } from './tool/webBluetoothBleScanner/in
|
|
|
35
35
|
export { KEYBOARD_CHATTER_TEST_TOOL } from './tool/keyboardChatterTest/index';
|
|
36
36
|
export { WEB_USB_SERIAL_MONITOR_TOOL } from './tool/webUsbSerialMonitor/index';
|
|
37
37
|
export { USB_POWER_BUDGET_CALCULATOR_TOOL } from './tool/usbPowerBudgetCalculator/index';
|
|
38
|
+
export { MOBILE_SENSOR_TEST_TOOL } from './tool/mobileSensorTest/index';
|
|
@@ -5,7 +5,7 @@ import { hardwareCategory } from '../data';
|
|
|
5
5
|
describe('Tool Validation Suite', () => {
|
|
6
6
|
describe('Library Registration', () => {
|
|
7
7
|
it('should have all tools in ALL_TOOLS', () => {
|
|
8
|
-
expect(ALL_TOOLS.length).toBe(
|
|
8
|
+
expect(ALL_TOOLS.length).toBe(19);
|
|
9
9
|
});
|
|
10
10
|
|
|
11
11
|
it('hardwareCategory should be defined', () => {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { Bibliography as SharedBibliography } from '@jjlmoya/utils-shared';
|
|
3
|
+
import type { KnownLocale } from '../../types';
|
|
4
|
+
import { mobileSensorTest } from './index';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
locale?: KnownLocale;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { locale = 'en' } = Astro.props;
|
|
11
|
+
const content = await mobileSensorTest.i18n[locale]?.();
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
{content && content.bibliography.length > 0 && <SharedBibliography links={content.bibliography} />}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { BibliographyEntry } from '../../types';
|
|
2
|
+
|
|
3
|
+
export const bibliography: BibliographyEntry[] = [
|
|
4
|
+
{
|
|
5
|
+
name: 'Apple Support - Calibrate the compass on iPhone',
|
|
6
|
+
url: 'https://support.apple.com/guide/iphone/iph1ac0b663/ios',
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
name: 'Android Developers - Position sensors',
|
|
10
|
+
url: 'https://developer.android.com/develop/sensors-and-location/sensors/sensors_position',
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
name: 'Android Developers - Motion sensors',
|
|
14
|
+
url: 'https://developer.android.com/develop/sensors-and-location/sensors/sensors_motion',
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
name: 'W3C - Motion Sensors Explainer',
|
|
18
|
+
url: 'https://www.w3.org/TR/motion-sensors/',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
name: 'WebKit - DeviceMotion and DeviceOrientation permission policy',
|
|
22
|
+
url: 'https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/',
|
|
23
|
+
},
|
|
24
|
+
];
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { KnownLocale } from '../../types';
|
|
3
|
+
import type { MobileSensorTestUI } from './ui';
|
|
4
|
+
import './mobile-sensor-test.css';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
locale?: KnownLocale;
|
|
8
|
+
ui?: Record<string, unknown>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const { ui } = Astro.props;
|
|
12
|
+
const t = (ui ?? {}) as MobileSensorTestUI;
|
|
13
|
+
const strings = JSON.stringify(t);
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
<div class="mst-shell" id="mst-root" data-ui={strings} data-state="ready">
|
|
17
|
+
<div class="mst-stage">
|
|
18
|
+
<div class="mst-privacy">
|
|
19
|
+
<strong>{t.privacyBadge}</strong>
|
|
20
|
+
<span>{t.privacyCopy}</span>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<button class="mst-start" type="button" id="mst-start">
|
|
24
|
+
<span>{t.startButton}</span>
|
|
25
|
+
<small>{t.permissionHint}</small>
|
|
26
|
+
</button>
|
|
27
|
+
|
|
28
|
+
<div class="mst-status" id="mst-status">{t.statusReady}</div>
|
|
29
|
+
|
|
30
|
+
<div class="mst-orbit" aria-label={t.cubeLabel}>
|
|
31
|
+
<div class="mst-cube" id="mst-cube">
|
|
32
|
+
<span class="mst-face mst-face-front"></span>
|
|
33
|
+
<span class="mst-face mst-face-back"></span>
|
|
34
|
+
<span class="mst-face mst-face-right"></span>
|
|
35
|
+
<span class="mst-face mst-face-left"></span>
|
|
36
|
+
<span class="mst-face mst-face-top"></span>
|
|
37
|
+
<span class="mst-face mst-face-bottom"></span>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
<div class="mst-panels">
|
|
43
|
+
<section class="mst-panel mst-bubble-card">
|
|
44
|
+
<div class="mst-panel-top">
|
|
45
|
+
<span>{t.bubblePanel}</span>
|
|
46
|
+
<strong id="mst-stability">{t.steadyLabel}</strong>
|
|
47
|
+
</div>
|
|
48
|
+
<div class="mst-bubble" aria-label={t.bubbleLabel}>
|
|
49
|
+
<span id="mst-bubble-dot"></span>
|
|
50
|
+
<i></i>
|
|
51
|
+
<b></b>
|
|
52
|
+
</div>
|
|
53
|
+
<div class="mst-readout">
|
|
54
|
+
<span>{t.levelOffsetLabel}</span>
|
|
55
|
+
<strong id="mst-level"></strong>
|
|
56
|
+
</div>
|
|
57
|
+
</section>
|
|
58
|
+
|
|
59
|
+
<section class="mst-panel">
|
|
60
|
+
<div class="mst-panel-top">
|
|
61
|
+
<span>{t.orientationPanel}</span>
|
|
62
|
+
<strong>{t.calibrationLabel}</strong>
|
|
63
|
+
</div>
|
|
64
|
+
<div class="mst-axis-grid">
|
|
65
|
+
<div><span>{t.alphaLabel}</span><strong id="mst-alpha"></strong></div>
|
|
66
|
+
<div><span>{t.betaLabel}</span><strong id="mst-beta"></strong></div>
|
|
67
|
+
<div><span>{t.gammaLabel}</span><strong id="mst-gamma"></strong></div>
|
|
68
|
+
</div>
|
|
69
|
+
</section>
|
|
70
|
+
|
|
71
|
+
<section class="mst-panel">
|
|
72
|
+
<div class="mst-panel-top">
|
|
73
|
+
<span>{t.motionPanel}</span>
|
|
74
|
+
<strong id="mst-motion-total"></strong>
|
|
75
|
+
</div>
|
|
76
|
+
<div class="mst-bars">
|
|
77
|
+
<label><span>{t.accelXLabel}</span><i><b id="mst-accel-x"></b></i><strong id="mst-accel-x-value"></strong></label>
|
|
78
|
+
<label><span>{t.accelYLabel}</span><i><b id="mst-accel-y"></b></i><strong id="mst-accel-y-value"></strong></label>
|
|
79
|
+
<label><span>{t.accelZLabel}</span><i><b id="mst-accel-z"></b></i><strong id="mst-accel-z-value"></strong></label>
|
|
80
|
+
</div>
|
|
81
|
+
<div class="mst-rotation-grid">
|
|
82
|
+
<div><span>{t.rotationAlphaLabel}</span><strong id="mst-rot-alpha"></strong></div>
|
|
83
|
+
<div><span>{t.rotationBetaLabel}</span><strong id="mst-rot-beta"></strong></div>
|
|
84
|
+
<div><span>{t.rotationGammaLabel}</span><strong id="mst-rot-gamma"></strong></div>
|
|
85
|
+
</div>
|
|
86
|
+
</section>
|
|
87
|
+
</div>
|
|
88
|
+
|
|
89
|
+
<script>
|
|
90
|
+
import { assessMobileSensors, clamp, safeNumber } from './logic';
|
|
91
|
+
|
|
92
|
+
const root = document.getElementById('mst-root') as HTMLElement | null;
|
|
93
|
+
const ui = JSON.parse(root?.dataset.ui ?? '{}') as Record<string, string>;
|
|
94
|
+
const startButton = document.getElementById('mst-start') as HTMLButtonElement | null;
|
|
95
|
+
const status = document.getElementById('mst-status');
|
|
96
|
+
const cube = document.getElementById('mst-cube') as HTMLElement | null;
|
|
97
|
+
const bubble = document.getElementById('mst-bubble-dot') as HTMLElement | null;
|
|
98
|
+
const stability = document.getElementById('mst-stability');
|
|
99
|
+
const fields = {
|
|
100
|
+
alpha: document.getElementById('mst-alpha'),
|
|
101
|
+
beta: document.getElementById('mst-beta'),
|
|
102
|
+
gamma: document.getElementById('mst-gamma'),
|
|
103
|
+
level: document.getElementById('mst-level'),
|
|
104
|
+
motionTotal: document.getElementById('mst-motion-total'),
|
|
105
|
+
accelX: document.getElementById('mst-accel-x') as HTMLElement | null,
|
|
106
|
+
accelY: document.getElementById('mst-accel-y') as HTMLElement | null,
|
|
107
|
+
accelZ: document.getElementById('mst-accel-z') as HTMLElement | null,
|
|
108
|
+
accelXValue: document.getElementById('mst-accel-x-value'),
|
|
109
|
+
accelYValue: document.getElementById('mst-accel-y-value'),
|
|
110
|
+
accelZValue: document.getElementById('mst-accel-z-value'),
|
|
111
|
+
rotAlpha: document.getElementById('mst-rot-alpha'),
|
|
112
|
+
rotBeta: document.getElementById('mst-rot-beta'),
|
|
113
|
+
rotGamma: document.getElementById('mst-rot-gamma'),
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const snapshot = {
|
|
117
|
+
alpha: 0,
|
|
118
|
+
beta: 0,
|
|
119
|
+
gamma: 0,
|
|
120
|
+
accelerationX: 0,
|
|
121
|
+
accelerationY: 0,
|
|
122
|
+
accelerationZ: 0,
|
|
123
|
+
rotationAlpha: 0,
|
|
124
|
+
rotationBeta: 0,
|
|
125
|
+
rotationGamma: 0,
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
function setText(el: HTMLElement | null, value: string) {
|
|
129
|
+
if (el) el.textContent = value;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function setState(state: string, label: string) {
|
|
133
|
+
if (root) root.dataset.state = state;
|
|
134
|
+
setText(status, label);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function formatDegrees(value: number) {
|
|
138
|
+
return `${value.toFixed(1)} deg`;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function setBar(el: HTMLElement | null, value: number) {
|
|
142
|
+
if (!el) return;
|
|
143
|
+
const width = clamp(Math.abs(value) / 24 * 100, 2, 100);
|
|
144
|
+
el.style.width = `${width}%`;
|
|
145
|
+
el.style.transform = value < 0 ? 'scaleX(-1)' : 'scaleX(1)';
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function render() {
|
|
149
|
+
const assessment = assessMobileSensors(snapshot);
|
|
150
|
+
const bubbleX = clamp(snapshot.gamma / 45 * 50, -42, 42);
|
|
151
|
+
const bubbleY = clamp(snapshot.beta / 45 * 50, -42, 42);
|
|
152
|
+
const stabilityText = ui[`${assessment.stability}Label`] ?? ui.steadyLabel ?? '';
|
|
153
|
+
|
|
154
|
+
setText(fields.alpha, formatDegrees(snapshot.alpha));
|
|
155
|
+
setText(fields.beta, formatDegrees(snapshot.beta));
|
|
156
|
+
setText(fields.gamma, formatDegrees(snapshot.gamma));
|
|
157
|
+
setText(fields.level, formatDegrees(assessment.levelOffset));
|
|
158
|
+
setText(fields.motionTotal, `${assessment.motionMagnitude.toFixed(1)} m/s2`);
|
|
159
|
+
setText(fields.accelXValue, snapshot.accelerationX.toFixed(1));
|
|
160
|
+
setText(fields.accelYValue, snapshot.accelerationY.toFixed(1));
|
|
161
|
+
setText(fields.accelZValue, snapshot.accelerationZ.toFixed(1));
|
|
162
|
+
setText(fields.rotAlpha, snapshot.rotationAlpha.toFixed(1));
|
|
163
|
+
setText(fields.rotBeta, snapshot.rotationBeta.toFixed(1));
|
|
164
|
+
setText(fields.rotGamma, snapshot.rotationGamma.toFixed(1));
|
|
165
|
+
setText(stability, stabilityText);
|
|
166
|
+
setBar(fields.accelX, snapshot.accelerationX);
|
|
167
|
+
setBar(fields.accelY, snapshot.accelerationY);
|
|
168
|
+
setBar(fields.accelZ, snapshot.accelerationZ);
|
|
169
|
+
|
|
170
|
+
if (bubble) bubble.style.transform = `translate(calc(-50% + ${bubbleX}px), calc(-50% + ${bubbleY}px))`;
|
|
171
|
+
if (cube) cube.style.transform = `rotateZ(${snapshot.alpha}deg) rotateX(${snapshot.beta}deg) rotateY(${snapshot.gamma}deg)`;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function onOrientation(event: DeviceOrientationEvent) {
|
|
175
|
+
snapshot.alpha = safeNumber(event.alpha);
|
|
176
|
+
snapshot.beta = safeNumber(event.beta);
|
|
177
|
+
snapshot.gamma = safeNumber(event.gamma);
|
|
178
|
+
render();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function onMotion(event: DeviceMotionEvent) {
|
|
182
|
+
snapshot.accelerationX = safeNumber(event.accelerationIncludingGravity?.x);
|
|
183
|
+
snapshot.accelerationY = safeNumber(event.accelerationIncludingGravity?.y);
|
|
184
|
+
snapshot.accelerationZ = safeNumber(event.accelerationIncludingGravity?.z);
|
|
185
|
+
snapshot.rotationAlpha = safeNumber(event.rotationRate?.alpha);
|
|
186
|
+
snapshot.rotationBeta = safeNumber(event.rotationRate?.beta);
|
|
187
|
+
snapshot.rotationGamma = safeNumber(event.rotationRate?.gamma);
|
|
188
|
+
render();
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function hasSensorSupport() {
|
|
192
|
+
return 'DeviceOrientationEvent' in window || 'DeviceMotionEvent' in window;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
async function requestEventPermission(eventConstructor: unknown): Promise<PermissionState> {
|
|
196
|
+
const eventWithPermission = eventConstructor as { requestPermission?: () => Promise<PermissionState> };
|
|
197
|
+
if (typeof eventWithPermission.requestPermission !== 'function') return 'granted';
|
|
198
|
+
return eventWithPermission.requestPermission();
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async function requestRequiredPermissions() {
|
|
202
|
+
const orientationPermission = await requestEventPermission(window.DeviceOrientationEvent);
|
|
203
|
+
const motionPermission = await requestEventPermission(window.DeviceMotionEvent);
|
|
204
|
+
|
|
205
|
+
return orientationPermission === 'granted' && motionPermission === 'granted';
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function activateSensors() {
|
|
209
|
+
window.addEventListener('deviceorientation', onOrientation);
|
|
210
|
+
window.addEventListener('devicemotion', onMotion);
|
|
211
|
+
startButton?.setAttribute('disabled', 'true');
|
|
212
|
+
setState('active', ui.statusActive ?? '');
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async function requestSensorAccess() {
|
|
216
|
+
if (!hasSensorSupport()) {
|
|
217
|
+
setState('unsupported', ui.statusUnsupported ?? '');
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
setState('waiting', ui.statusWaiting ?? '');
|
|
222
|
+
|
|
223
|
+
try {
|
|
224
|
+
if (!(await requestRequiredPermissions())) {
|
|
225
|
+
setState('denied', ui.statusDenied ?? '');
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
activateSensors();
|
|
230
|
+
} catch {
|
|
231
|
+
setState('denied', ui.statusDenied ?? '');
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
startButton?.addEventListener('click', () => {
|
|
236
|
+
void requestSensorAccess();
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
render();
|
|
240
|
+
</script>
|
|
241
|
+
</div>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { HardwareToolEntry, ToolLocaleContent } from '../../types';
|
|
2
|
+
import type { MobileSensorTestUI } from './ui';
|
|
3
|
+
|
|
4
|
+
export type MobileSensorTestLocaleContent = ToolLocaleContent<MobileSensorTestUI>;
|
|
5
|
+
|
|
6
|
+
export const mobileSensorTest: HardwareToolEntry<MobileSensorTestUI> = {
|
|
7
|
+
id: 'mobile-sensor-test',
|
|
8
|
+
icons: {
|
|
9
|
+
bg: 'mdi:cellphone-cog',
|
|
10
|
+
fg: 'mdi:axis-arrow',
|
|
11
|
+
},
|
|
12
|
+
i18n: {
|
|
13
|
+
de: () => import('./i18n/de').then((m) => m.content),
|
|
14
|
+
en: () => import('./i18n/en').then((m) => m.content),
|
|
15
|
+
es: () => import('./i18n/es').then((m) => m.content),
|
|
16
|
+
fr: () => import('./i18n/fr').then((m) => m.content),
|
|
17
|
+
id: () => import('./i18n/id').then((m) => m.content),
|
|
18
|
+
it: () => import('./i18n/it').then((m) => m.content),
|
|
19
|
+
ja: () => import('./i18n/ja').then((m) => m.content),
|
|
20
|
+
ko: () => import('./i18n/ko').then((m) => m.content),
|
|
21
|
+
nl: () => import('./i18n/nl').then((m) => m.content),
|
|
22
|
+
pl: () => import('./i18n/pl').then((m) => m.content),
|
|
23
|
+
pt: () => import('./i18n/pt').then((m) => m.content),
|
|
24
|
+
ru: () => import('./i18n/ru').then((m) => m.content),
|
|
25
|
+
sv: () => import('./i18n/sv').then((m) => m.content),
|
|
26
|
+
tr: () => import('./i18n/tr').then((m) => m.content),
|
|
27
|
+
zh: () => import('./i18n/zh').then((m) => m.content),
|
|
28
|
+
},
|
|
29
|
+
};
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
|
|
2
|
+
import type { ToolLocaleContent } from '../../../types';
|
|
3
|
+
import type { MobileSensorTestUI } from '../ui';
|
|
4
|
+
import { bibliography } from '../bibliography';
|
|
5
|
+
|
|
6
|
+
const slug = 'mobiler-sensor-test';
|
|
7
|
+
const title = 'Mobiler Sensor Test';
|
|
8
|
+
const description = 'Führen Sie einen Online-Test für Gyroskop, Beschleunigungssensor, Bewegungssensor und digitale Wasserwaage auf Ihrem Handy durch, um Neigung, Drehung, Drift und Sensor-Kalibrierungsprobleme zu überprüfen.';
|
|
9
|
+
|
|
10
|
+
const faqData = [
|
|
11
|
+
{
|
|
12
|
+
question: 'Wie kann ich mein Handy-Gyroskop online testen?',
|
|
13
|
+
answer: 'Öffnen Sie den Test auf dem Handy, tippen Sie auf Kalibrierung starten, erlauben Sie bei Aufforderung den Zugriff auf die Bewegungssensoren und drehen und neigen Sie dann das Gerät. Ein funktionierendes Gyroskop und ein Orientierungssensor sollten Alpha, Beta und Gamma gleichmäßig aktualisieren, ohne einzufrieren oder wild zu springen.',
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
question: 'Kann ich den Beschleunigungssensor mit einer Wasserwaage testen?',
|
|
17
|
+
answer: 'Ja. Legen Sie das Handy nach dem Start des Tests auf einen flachen Tisch. Die Blase sollte sich nahe der Mitte einpendeln und die Beschleunigungswerte sollten stabil bleiben. Wenn die Blase stark driftet, während das Handy still liegt, muss der Beschleunigungssensor möglicherweise kalibriert werden oder die Hülle, der Tisch oder die Gerätehardware stören.',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
question: 'Warum fragt das iPhone nach Bewegungserlaubnis?',
|
|
21
|
+
answer: 'Safari auf iPhone und iPad erfordert eine Berührung, bevor Websites auf Bewegungs- und Orientierungssensoren zugreifen können. Wenn die Erlaubnis verweigert wird, kann der Test keine Gyroskop- oder Beschleunigungsdaten lesen, bis Sie den Bewegungszugriff erlauben.',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
question: 'Kann dies einen defekten Kompass reparieren oder kalibrieren?',
|
|
25
|
+
answer: 'Kein Browser-Tool kann Hardware reparieren oder die System-Kompasskalibrierung erzwingen. Dieser Test hilft Ihnen, Symptome zu erkennen: eingefrorene Messwerte, verrauschtes Signal, Drift, fehlende Berechtigungen oder einen Browser, der die Sensoren nicht bereitstellt.',
|
|
26
|
+
},
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
const howToData = [
|
|
30
|
+
{ name: 'Öffnen Sie den Test auf Ihrem Handy', text: 'Verwenden Sie dasselbe iPhone, iPad, Android-Handy oder Tablet, das Sie diagnostizieren möchten. Desktop-Browser haben in der Regel keine Bewegungssensoren.' },
|
|
31
|
+
{ name: 'Bewegungszugriff erlauben', text: 'Tippen Sie auf Kalibrierung starten und akzeptieren Sie die Aufforderung zur Bewegungs- oder Orientierungserlaubnis, falls Ihr Browser eine anzeigt.' },
|
|
32
|
+
{ name: 'Neigung und Drehung testen', text: 'Kippen Sie das Handy nach vorne, rollen Sie es nach links und rechts, dann drehen Sie es flach auf einem Tisch. Achten Sie auf gleichmäßige Änderungen von Alpha, Beta, Gamma und Beschleunigung.' },
|
|
33
|
+
{ name: 'Drift auf einer flachen Oberfläche prüfen', text: 'Lassen Sie das Handy mehrere Sekunden lang still liegen. Ein gesunder Sensor sollte sich stabilisieren, anstatt ständig zu wandern, Spitzen zu zeigen oder einzufrieren.' },
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
const faqSchema: WithContext<FAQPage> = {
|
|
37
|
+
'@context': 'https://schema.org',
|
|
38
|
+
'@type': 'FAQPage',
|
|
39
|
+
mainEntity: faqData.map((item) => ({
|
|
40
|
+
'@type': 'Question',
|
|
41
|
+
name: item.question,
|
|
42
|
+
acceptedAnswer: { '@type': 'Answer', text: item.answer },
|
|
43
|
+
})),
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const howToSchema: WithContext<HowTo> = {
|
|
47
|
+
'@context': 'https://schema.org',
|
|
48
|
+
'@type': 'HowTo',
|
|
49
|
+
name: title,
|
|
50
|
+
description,
|
|
51
|
+
step: howToData.map((step, i) => ({
|
|
52
|
+
'@type': 'HowToStep',
|
|
53
|
+
position: i + 1,
|
|
54
|
+
name: step.name,
|
|
55
|
+
text: step.text,
|
|
56
|
+
})),
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const appSchema: WithContext<SoftwareApplication> = {
|
|
60
|
+
'@context': 'https://schema.org',
|
|
61
|
+
'@type': 'SoftwareApplication',
|
|
62
|
+
name: title,
|
|
63
|
+
description,
|
|
64
|
+
applicationCategory: 'UtilityApplication',
|
|
65
|
+
operatingSystem: 'All',
|
|
66
|
+
offers: { '@type': 'Offer', price: '0', priceCurrency: 'USD' },
|
|
67
|
+
inLanguage: 'de',
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export const content: ToolLocaleContent<MobileSensorTestUI> = {
|
|
71
|
+
slug,
|
|
72
|
+
title,
|
|
73
|
+
description,
|
|
74
|
+
faq: faqData,
|
|
75
|
+
howTo: howToData,
|
|
76
|
+
schemas: [faqSchema, howToSchema, appSchema],
|
|
77
|
+
bibliography,
|
|
78
|
+
seo: [
|
|
79
|
+
{ type: 'title', text: 'Online-Gyroskop- und Beschleunigungssensor-Test für Handys', level: 2 },
|
|
80
|
+
{
|
|
81
|
+
type: 'paragraph',
|
|
82
|
+
html: 'Verwenden Sie diesen Handy-Sensor-Test, wenn sich die Bildschirmdrehung falsch anfühlt, Spiele oder AR-Apps driften, eine Wasserwaagen-App ungenau wirkt, die Navigation in die falsche Richtung zeigt oder das Handy nicht korrekt auf Neigung reagiert. Der Test zeigt live das Verhalten von Gyroskop, Beschleunigungssensor, Drehung und Wasserwaage, damit Sie ein Browser-Berechtigungsproblem von einem echten Sensor- oder Kalibrierungsproblem unterscheiden können.',
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
type: 'stats',
|
|
86
|
+
items: [
|
|
87
|
+
{ label: 'Hauptsuchabsicht', value: 'Gyroskop online testen' },
|
|
88
|
+
{ label: 'Prüft auch', value: 'Beschleunigungssensor-Drift' },
|
|
89
|
+
{ label: 'Bestes Gerät', value: 'Handy oder Tablet' },
|
|
90
|
+
],
|
|
91
|
+
},
|
|
92
|
+
{ type: 'title', text: 'Was Ihnen die einzelnen Sensorwerte sagen', level: 3 },
|
|
93
|
+
{
|
|
94
|
+
type: 'table',
|
|
95
|
+
headers: ['Messwert', 'Nützlich für', 'Warnsignale'],
|
|
96
|
+
rows: [
|
|
97
|
+
['Alpha', 'Prüfung der Drehung um die vertikale Achse, nützlich für kompassartige Bewegungen und Kursänderungen.', 'Ändert sich nicht beim flachen Drehen des Handys, springt in großen Schritten oder bleibt bei Null eingefroren.'],
|
|
98
|
+
['Beta', 'Prüfung der Vorwärts-Rückwärts-Neigung für Bildschirmdrehung, Spiele, Kamera-Nivellierung und AR-Steuerung.', 'Bewegt sich in die falsche Richtung, bleibt bei einem Wert hängen oder driftet ständig, während das Handy still liegt.'],
|
|
99
|
+
['Gamma', 'Prüfung der Links-Rechts-Rollbewegung für Querformat-Drehung, Rennspiele, Wasserwaagen-Apps und Stabilisatoren.', 'Eine Seite reagiert anders, die Werte sind verrauscht oder die Blase zentriert sich nie auf einem flachen Tisch.'],
|
|
100
|
+
['Beschleunigung X/Y/Z', 'Prüfung der Beschleunigungssensor-Reaktion, Schüttelerkennung, Schwerkraftrichtung und Bewegungsstabilität.', 'Große Spitzen im Stillstand, keine Reaktion auf Bewegung oder instabile Messwerte nach Entfernen der Hülle.'],
|
|
101
|
+
],
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
type: 'diagnostic',
|
|
105
|
+
variant: 'warning',
|
|
106
|
+
title: 'Symptome, bei deren Diagnose dieser Test hilft',
|
|
107
|
+
html: '<p>Nutzen Sie die Live-Werte, um folgende Probleme zu untersuchen: Nicht funktionierende automatische Drehung, ein verzögert wirkendes Gyroskop, Beschleunigungssensor-Drift, Kompass-Kalibrierungswarnungen, wegrutschendes AR-Tracking, Kamera-Wasserwaagenfehler, Bewegungssteuerungen, die zu einer Seite ziehen, oder ein Handy, das Bewegung nur in nativen Apps, aber nicht im Browser meldet.</p>',
|
|
108
|
+
},
|
|
109
|
+
{ type: 'title', text: 'Gyroskop vs. Beschleunigungssensor vs. Kompass', level: 3 },
|
|
110
|
+
{
|
|
111
|
+
type: 'comparative',
|
|
112
|
+
items: [
|
|
113
|
+
{ title: 'Gyroskop', description: 'Misst die Drehgeschwindigkeit. Wichtig für AR, Spiele, Kamerastabilisierung, Bewegungssteuerung und gleichmäßige Orientierungsänderungen.' },
|
|
114
|
+
{ title: 'Beschleunigungssensor', description: 'Misst Beschleunigung und Schwerkraftrichtung. Ermöglicht Neigung, Schüttelerkennung, Schrittzählung und das Verhalten der digitalen Wasserwaage.' },
|
|
115
|
+
{ title: 'Kompass / Magnetometer', description: 'Hilft bei der Schätzung des Kurses, kann aber durch Magnete, Metallgegenstände, Autohalterungen, Hüllen, Lautsprecher, Laptops und nahe Elektronik gestört werden.' },
|
|
116
|
+
],
|
|
117
|
+
},
|
|
118
|
+
{ type: 'title', text: 'So testen Sie die Sensorkalibrierung richtig', level: 3 },
|
|
119
|
+
{
|
|
120
|
+
type: 'list',
|
|
121
|
+
items: [
|
|
122
|
+
'Entfernen Sie vor dem Test magnetische Hüllen, MagSafe-Geldbörsen, Metallhalterungen, Controller-Clips und Zubehör.',
|
|
123
|
+
'Legen Sie das Handy auf einen stabilen, flachen Tisch und warten Sie mehrere Sekunden, bevor Sie die Drift beurteilen.',
|
|
124
|
+
'Drehen Sie das Handy langsam durch jede Achse, anstatt es sofort zu schütteln.',
|
|
125
|
+
'Vergleichen Sie Safari oder Chrome mit einer nativen Sensor- oder Kompass-App, wenn der Browser keine Daten anzeigt.',
|
|
126
|
+
'Starten Sie das Gerät neu und wiederholen Sie den Test, wenn die Messwerte in mehreren Apps eingefroren sind.',
|
|
127
|
+
],
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
type: 'tip',
|
|
131
|
+
title: 'Hinweis zu Berechtigungen bei iPhone und Android',
|
|
132
|
+
html: 'Auf iPhone und iPad kann Safari nach einer Berührung nach Bewegungs- und Orientierungserlaubnis fragen. Unter Android stellt Chrome Bewegungssensoren in der Regel direkter bereit, aber Datenschutzeinstellungen, Browser-Flags, Energiesparmodi und eingebettete Webviews können die Sensordaten immer noch blockieren oder reduzieren.',
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
type: 'table',
|
|
136
|
+
headers: ['Problem', 'Wahrscheinliche Ursache', 'Nächster Schritt'],
|
|
137
|
+
rows: [
|
|
138
|
+
['Keine Werte ändern sich', 'Erlaubnis verweigert, nicht unterstützter Browser, Desktop-Gerät oder eingeschränkte Webview.', 'Auf dem Handy selbst öffnen, Safari oder Chrome verwenden, Bewegungszugriff erlauben und die Seite neu laden.'],
|
|
139
|
+
['Blase driftet auf einem Tisch', 'Kalibrierungsproblem, unebene Oberfläche, Hüllen-Interferenz oder Beschleunigungssensor-Rauschen.', 'Hülle entfernen, eine bekanntermaßen ebene Fläche verwenden, neu starten und mit einer nativen Wasserwaagen-App vergleichen.'],
|
|
140
|
+
['Kompassrichtung ist falsch', 'Magnetische Interferenz oder System-Kompasskalibrierung.', 'Von Metall/Elektronik entfernen und den Kompass-Kalibrierungsablauf des Betriebssystems verwenden.'],
|
|
141
|
+
['Werte springen oder zeigen Spitzen', 'Sensorrauschen, beschädigte Hardware, aggressives Browser-Drosseling oder plötzliche physische Bewegung.', 'Im Stillstand testen, rechenintensive Apps schließen und mit einem anderen Browser oder einer nativen App vergleichen.'],
|
|
142
|
+
],
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
type: 'summary',
|
|
146
|
+
title: 'Wofür dieser Test gut ist',
|
|
147
|
+
items: [
|
|
148
|
+
'Testen eines Handy-Gyroskops online, ohne eine App zu installieren.',
|
|
149
|
+
'Überprüfen der Beschleunigungssensor-Drift mit einer Live-Wasserwaage.',
|
|
150
|
+
'Herausfinden, ob Bewegungssteuerungen aufgrund von Hardware, Kalibrierung, Berechtigungen oder Browser-Unterstützung versagen.',
|
|
151
|
+
'Vorbereiten eines Handys für AR, Spiele, Kamera-Nivellierung, Navigation oder Fehlerbehebung bei der Bildschirmdrehung.',
|
|
152
|
+
],
|
|
153
|
+
},
|
|
154
|
+
],
|
|
155
|
+
ui: {
|
|
156
|
+
startButton: 'Kalibrierung starten',
|
|
157
|
+
permissionHint: 'Einmal tippen, um Bewegungssensoren freizuschalten',
|
|
158
|
+
privacyBadge: 'Lokale Sensordaten',
|
|
159
|
+
privacyCopy: 'Orientierungs- und Bewegungswerte bleiben innerhalb dieser Browser-Sitzung.',
|
|
160
|
+
orientationPanel: 'Orientierung',
|
|
161
|
+
motionPanel: 'Bewegung',
|
|
162
|
+
bubblePanel: 'Digitale Wasserwaage',
|
|
163
|
+
statusReady: 'Bereit für Sensorerlaubnis',
|
|
164
|
+
statusWaiting: 'Warte auf Browser-Erlaubnis',
|
|
165
|
+
statusDenied: 'Sensorzugriff wurde verweigert oder ist nicht verfügbar',
|
|
166
|
+
statusUnsupported: 'Bewegungssensoren werden von diesem Browser nicht bereitgestellt',
|
|
167
|
+
statusActive: 'Live-Sensorstrom aktiv',
|
|
168
|
+
steadyLabel: 'Ruhig',
|
|
169
|
+
movingLabel: 'In Bewegung',
|
|
170
|
+
shakingLabel: 'Schüttelnd',
|
|
171
|
+
alphaLabel: 'Alpha',
|
|
172
|
+
betaLabel: 'Beta',
|
|
173
|
+
gammaLabel: 'Gamma',
|
|
174
|
+
accelXLabel: 'X',
|
|
175
|
+
accelYLabel: 'Y',
|
|
176
|
+
accelZLabel: 'Z',
|
|
177
|
+
rotationAlphaLabel: 'Rot A',
|
|
178
|
+
rotationBetaLabel: 'Rot B',
|
|
179
|
+
rotationGammaLabel: 'Rot G',
|
|
180
|
+
levelOffsetLabel: 'Wasserwaagen-Abweichung',
|
|
181
|
+
motionMagnitudeLabel: 'Bewegungsstärke',
|
|
182
|
+
cubeLabel: '3D-Geräte-Orientierungswürfel',
|
|
183
|
+
bubbleLabel: 'Wasserwaagen-Anzeige',
|
|
184
|
+
calibrationLabel: 'Live-Grad',
|
|
185
|
+
},
|
|
186
|
+
};
|