@jjlmoya/utils-hardware 1.26.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.
Files changed (55) hide show
  1. package/package.json +1 -1
  2. package/src/category/index.ts +3 -1
  3. package/src/entries.ts +7 -1
  4. package/src/index.ts +2 -0
  5. package/src/tests/locale_completeness.test.ts +1 -1
  6. package/src/tests/tool_validation.test.ts +1 -1
  7. package/src/tool/mobileSensorTest/bibliography.astro +14 -0
  8. package/src/tool/mobileSensorTest/bibliography.ts +24 -0
  9. package/src/tool/mobileSensorTest/component.astro +241 -0
  10. package/src/tool/mobileSensorTest/entry.ts +29 -0
  11. package/src/tool/mobileSensorTest/i18n/de.ts +186 -0
  12. package/src/tool/mobileSensorTest/i18n/en.ts +186 -0
  13. package/src/tool/mobileSensorTest/i18n/es.ts +186 -0
  14. package/src/tool/mobileSensorTest/i18n/fr.ts +186 -0
  15. package/src/tool/mobileSensorTest/i18n/id.ts +186 -0
  16. package/src/tool/mobileSensorTest/i18n/it.ts +186 -0
  17. package/src/tool/mobileSensorTest/i18n/ja.ts +186 -0
  18. package/src/tool/mobileSensorTest/i18n/ko.ts +186 -0
  19. package/src/tool/mobileSensorTest/i18n/nl.ts +186 -0
  20. package/src/tool/mobileSensorTest/i18n/pl.ts +186 -0
  21. package/src/tool/mobileSensorTest/i18n/pt.ts +186 -0
  22. package/src/tool/mobileSensorTest/i18n/ru.ts +186 -0
  23. package/src/tool/mobileSensorTest/i18n/sv.ts +186 -0
  24. package/src/tool/mobileSensorTest/i18n/tr.ts +186 -0
  25. package/src/tool/mobileSensorTest/i18n/zh.ts +186 -0
  26. package/src/tool/mobileSensorTest/index.ts +11 -0
  27. package/src/tool/mobileSensorTest/logic.ts +39 -0
  28. package/src/tool/mobileSensorTest/mobile-sensor-test.css +460 -0
  29. package/src/tool/mobileSensorTest/seo.astro +15 -0
  30. package/src/tool/mobileSensorTest/ui.ts +31 -0
  31. package/src/tool/usbPowerBudgetCalculator/bibliography.astro +14 -0
  32. package/src/tool/usbPowerBudgetCalculator/bibliography.ts +16 -0
  33. package/src/tool/usbPowerBudgetCalculator/component.astro +266 -0
  34. package/src/tool/usbPowerBudgetCalculator/entry.ts +29 -0
  35. package/src/tool/usbPowerBudgetCalculator/i18n/de.ts +169 -0
  36. package/src/tool/usbPowerBudgetCalculator/i18n/en.ts +169 -0
  37. package/src/tool/usbPowerBudgetCalculator/i18n/es.ts +169 -0
  38. package/src/tool/usbPowerBudgetCalculator/i18n/fr.ts +169 -0
  39. package/src/tool/usbPowerBudgetCalculator/i18n/id.ts +169 -0
  40. package/src/tool/usbPowerBudgetCalculator/i18n/it.ts +169 -0
  41. package/src/tool/usbPowerBudgetCalculator/i18n/ja.ts +169 -0
  42. package/src/tool/usbPowerBudgetCalculator/i18n/ko.ts +169 -0
  43. package/src/tool/usbPowerBudgetCalculator/i18n/nl.ts +169 -0
  44. package/src/tool/usbPowerBudgetCalculator/i18n/pl.ts +169 -0
  45. package/src/tool/usbPowerBudgetCalculator/i18n/pt.ts +169 -0
  46. package/src/tool/usbPowerBudgetCalculator/i18n/ru.ts +169 -0
  47. package/src/tool/usbPowerBudgetCalculator/i18n/sv.ts +169 -0
  48. package/src/tool/usbPowerBudgetCalculator/i18n/tr.ts +169 -0
  49. package/src/tool/usbPowerBudgetCalculator/i18n/zh.ts +169 -0
  50. package/src/tool/usbPowerBudgetCalculator/index.ts +11 -0
  51. package/src/tool/usbPowerBudgetCalculator/logic.ts +81 -0
  52. package/src/tool/usbPowerBudgetCalculator/seo.astro +15 -0
  53. package/src/tool/usbPowerBudgetCalculator/ui.ts +32 -0
  54. package/src/tool/usbPowerBudgetCalculator/usb-power-budget-calculator.css +484 -0
  55. package/src/tools.ts +3 -1
@@ -0,0 +1,169 @@
1
+ import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
2
+ import type { ToolLocaleContent } from '../../../types';
3
+ import type { UsbPowerBudgetCalculatorUI } from '../ui';
4
+ import { bibliography } from '../bibliography';
5
+
6
+ const slug = 'usb-power-budget-calculator';
7
+ const title = 'USB 功率预算计算器';
8
+ const description = '检查USB端口、充电器、集线器、线缆或USB-C PD协议是否能在预留余量和线缆压降之后安全地为您的设备供电。';
9
+
10
+ const faqData = [
11
+ {
12
+ question: '如何知道USB端口能否为我的设备供电?',
13
+ answer: '将设备总功率与USB电源功率进行比较,预留余量并估算线缆压降。当线缆较长、较细或在5伏电压下传输较大电流时,即便标称功率看起来很高,配置也可能失败。',
14
+ },
15
+ {
16
+ question: '为什么线缆长度对USB供电很重要?',
17
+ answer: '电流流经铜线会产生压降。长线缆和细导体的电阻更大,因此设备接收到的电压可能低于充电器提供的电压。这对树莓派开发板、硬盘、LED灯带、扩展坞和总线供电集线器尤为重要。',
18
+ },
19
+ {
20
+ question: '应该使用多大的余量?',
21
+ answer: '普通电子设备至少使用20%,电机、驱动器、无线电或具有突发负载的开发板使用30%,如果适配器质量未知或设备需要连续运行,则需要更多的余量。',
22
+ },
23
+ {
24
+ question: '这能替代USB-C PD协商测试吗?',
25
+ answer: '不能。计算器检查电气预算,不验证充电器、线缆e-marker、设备或集线器是否实际协商了特定的Power Delivery协议。',
26
+ },
27
+ ];
28
+
29
+ const howToData = [
30
+ { name: '选择电源协议', text: '选择常见的USB或USB-C PD协议,或手动编辑电压和电流。' },
31
+ { name: '描述线缆', text: '输入线缆长度和导体线规。高AWG号的细线会导致更大的压降。' },
32
+ { name: '添加负载', text: '输入单个设备负载(瓦特)和共享同一电源的设备数量。' },
33
+ { name: '读取状态', text: '使用状态、线缆压降、最终电压、利用率和余量来决定配置是否安全。' },
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: 'zh',
68
+ };
69
+
70
+ export const content: ToolLocaleContent<UsbPowerBudgetCalculatorUI> = {
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: 'USB供电是预算,不是标签', level: 2 },
80
+ {
81
+ type: 'paragraph',
82
+ html: '标有15W、45W或100W的USB充电器描述的是电源在合适条件下能够提供的功率。您的设备只能看到经过协议协商、电流限制、线缆电阻、连接器质量、集线器损耗和负载峰值之后的结果。此计算器关注实际的电气问题:在线缆压降和预留余量之后,是否还有足够的功率来运行您想要的硬件?',
83
+ },
84
+ {
85
+ type: 'stats',
86
+ items: [
87
+ { label: 'USB 2.0 默认电流', value: '0.5 A' },
88
+ { label: 'USB-C 5V 默认最大值', value: '3 A' },
89
+ { label: '建议预留', value: '20%+' },
90
+ ],
91
+ },
92
+ { type: 'title', text: '如何解读结果', level: 3 },
93
+ {
94
+ type: 'table',
95
+ headers: ['状态', '含义', '最佳下一步'],
96
+ rows: [
97
+ ['安全', '负载在选定的预留余量后符合电源额定值,且估算的设备电压保持正常。', '使用此配置,但如果适配器较小或封闭,请注意散热。'],
98
+ ['紧张', '负载接近预留限制,或线缆压降开始变得显著。', '缩短线缆、选择更粗的导体、降低负载,或切换到更高的功率协议。'],
99
+ ['超出预算', '负载超出可用电源功率,或设备端电压可能过低。', '使用更强的充电器、带供电的集线器、更短的线缆,或使用能协商更高USB-C PD电压的设备。'],
100
+ ],
101
+ },
102
+ {
103
+ type: 'diagnostic',
104
+ variant: 'warning',
105
+ title: '瓦数足够但设备仍然重启时',
106
+ html: '<p>启动电流可能远高于设备标签上印的平均瓦数。在相同瓦数下,5V电源比20V PD协议更快损失电压,因为它必须承载更大的电流。许多低成本线缆即使外皮看起来很厚,也使用细的电源导体,而总线供电集线器在所有下游设备之间共享一个上游预算。</p>',
107
+ },
108
+ { type: 'title', text: '线缆压降简明解释', level: 3 },
109
+ {
110
+ type: 'paragraph',
111
+ html: '压降是电流流经线缆电阻时产生的损耗。USB供电在电源路径中有两个导体,因此计算器使用往返长度。一米长的线缆在电源回路中电气上相当于两米的铜。AWG号越小越粗,通常更适合大电流负载。',
112
+ },
113
+ {
114
+ type: 'comparative',
115
+ items: [
116
+ { title: '短粗线缆', description: '最适合树莓派开发板、SSD外壳、开发套件和具有突发电流需求的USB-C扩展坞。' },
117
+ { title: '长细线缆', description: '可接受用于低功耗传感器或慢速充电,但对驱动器、LED负载和计算开发板有风险。' },
118
+ { title: '更高电压PD', description: '在相同瓦数下降低电流,从而降低线缆损耗,但前提是电源、线缆和设备能协商。' },
119
+ ],
120
+ },
121
+ {
122
+ type: 'tip',
123
+ title: '实用规则',
124
+ html: '如果计算器显示配置紧张,请将其视为现场警告。USB故障通常在表现为明显的电源问题之前,以随机断开、欠压、慢速充电、音频噪声或存储错误的形式出现。',
125
+ },
126
+ {
127
+ type: 'summary',
128
+ title: '此计算器最适合',
129
+ items: [
130
+ '规划USB集线器、单板计算机、外置硬盘、开发板、灯具、风扇和小型实验室设置。',
131
+ '比较USB 2.0、USB 3.x、USB-C和USB Power Delivery电源协议。',
132
+ '估算线缆对于负载是否太长或太细。',
133
+ '在购买充电器或带供电的集线器之前选择合理的预留余量。',
134
+ ],
135
+ },
136
+ ],
137
+ ui: {
138
+ profileLabel: 'USB电源协议',
139
+ metricUnits: '公制',
140
+ imperialUnits: '美制',
141
+ voltageLabel: '电源电压 (V)',
142
+ currentLabel: '电源电流 (A)',
143
+ cableLengthLabel: '线缆长度',
144
+ wireGaugeLabel: '电源线规格',
145
+ deviceLoadLabel: '每设备负载 (W)',
146
+ devicesLabel: '设备数',
147
+ headroomLabel: '预留余量 (%)',
148
+ sourcePower: '电源功率',
149
+ requiredPower: '所需功率',
150
+ cableDrop: '线缆压降',
151
+ deviceVoltage: '设备电压',
152
+ headroom: '余量',
153
+ utilization: '利用率',
154
+ safeStatus: '功率预算看起来安全',
155
+ tightStatus: '功率预算紧张',
156
+ overStatus: '超出预算或存在压降风险',
157
+ safeAdvice: '负载在选定余量内合适。使用优质线缆,长时间运行时检查热量。',
158
+ tightAdvice: '您接近极限。减少线缆长度、使用更粗的导体、降低负载或选择更强的协议。',
159
+ overAdvice: '此配置可能发生欠压或降频。使用带供电的集线器、更强的适配器或更高电压的USB-C PD协议。',
160
+ busLane: 'USB电源',
161
+ loadLane: '设备负载',
162
+ cableLane: '压降',
163
+ boardEyebrow: '实时USB电源路径',
164
+ sourceSocket: '电源插座',
165
+ deviceSocket: '硬件负载',
166
+ energyFlow: '能量流',
167
+ reservedLabel: '预留后可用',
168
+ },
169
+ };
@@ -0,0 +1,11 @@
1
+ import { usbPowerBudgetCalculator } from './entry';
2
+ import type { ToolDefinition } from '../../types';
3
+
4
+ export * from './entry';
5
+
6
+ export const USB_POWER_BUDGET_CALCULATOR_TOOL: ToolDefinition = {
7
+ entry: usbPowerBudgetCalculator,
8
+ Component: () => import('./component.astro'),
9
+ SEOComponent: () => import('./seo.astro'),
10
+ BibliographyComponent: () => import('./bibliography.astro'),
11
+ };
@@ -0,0 +1,81 @@
1
+ export type UsbPowerProfile = 'usb2' | 'usb3' | 'bc12' | 'usbC15' | 'usbC27' | 'pd45' | 'pd65' | 'pd100';
2
+
3
+ export interface UsbPowerInput {
4
+ sourceVoltage: number;
5
+ sourceCurrent: number;
6
+ cableLengthMeters: number;
7
+ wireGaugeAwg: number;
8
+ deviceLoadWatts: number;
9
+ devices: number;
10
+ headroomPercent: number;
11
+ }
12
+
13
+ export interface UsbPowerResult {
14
+ sourceWatts: number;
15
+ requiredWatts: number;
16
+ availableWatts: number;
17
+ headroomWatts: number;
18
+ cableDropVolts: number;
19
+ deviceEndVoltage: number;
20
+ utilizationPercent: number;
21
+ status: 'safe' | 'tight' | 'over';
22
+ }
23
+
24
+ export const usbPowerProfiles: Record<UsbPowerProfile, { label: string; voltage: number; current: number }> = {
25
+ usb2: { label: 'USB 2.0 500 mA', voltage: 5, current: 0.5 },
26
+ usb3: { label: 'USB 3.x 900 mA', voltage: 5, current: 0.9 },
27
+ bc12: { label: 'BC 1.2 1.5 A', voltage: 5, current: 1.5 },
28
+ usbC15: { label: 'USB-C 15 W', voltage: 5, current: 3 },
29
+ usbC27: { label: 'USB-C 27 W', voltage: 9, current: 3 },
30
+ pd45: { label: 'USB PD 45 W', voltage: 15, current: 3 },
31
+ pd65: { label: 'USB PD 65 W', voltage: 20, current: 3.25 },
32
+ pd100: { label: 'USB PD 100 W', voltage: 20, current: 5 },
33
+ };
34
+
35
+ const copperOhmsPerMeterByAwg: Record<number, number> = {
36
+ 20: 0.0333,
37
+ 22: 0.053,
38
+ 24: 0.0842,
39
+ 26: 0.134,
40
+ 28: 0.213,
41
+ 30: 0.339,
42
+ };
43
+
44
+ export function metersToFeet(meters: number): number {
45
+ return meters * 3.28084;
46
+ }
47
+
48
+ export function feetToMeters(feet: number): number {
49
+ return feet / 3.28084;
50
+ }
51
+
52
+ export function calculateUsbPowerBudget(input: UsbPowerInput): UsbPowerResult {
53
+ const sourceWatts = input.sourceVoltage * input.sourceCurrent;
54
+ const requiredWatts = input.deviceLoadWatts * input.devices;
55
+ const availableWatts = sourceWatts * (1 - input.headroomPercent / 100);
56
+ const headroomWatts = availableWatts - requiredWatts;
57
+ const loadCurrent = input.sourceVoltage > 0 ? requiredWatts / input.sourceVoltage : 0;
58
+ const conductorOhms = copperOhmsPerMeterByAwg[input.wireGaugeAwg] ?? copperOhmsPerMeterByAwg[28];
59
+ const roundTripOhms = conductorOhms * input.cableLengthMeters * 2;
60
+ const cableDropVolts = loadCurrent * roundTripOhms;
61
+ const deviceEndVoltage = Math.max(0, input.sourceVoltage - cableDropVolts);
62
+ const utilizationPercent = sourceWatts > 0 ? (requiredWatts / sourceWatts) * 100 : 0;
63
+ let status: UsbPowerResult['status'] = 'safe';
64
+
65
+ if (requiredWatts > availableWatts || deviceEndVoltage < input.sourceVoltage * 0.9) {
66
+ status = 'over';
67
+ } else if (requiredWatts > availableWatts * 0.85 || deviceEndVoltage < input.sourceVoltage * 0.94) {
68
+ status = 'tight';
69
+ }
70
+
71
+ return {
72
+ sourceWatts,
73
+ requiredWatts,
74
+ availableWatts,
75
+ headroomWatts,
76
+ cableDropVolts,
77
+ deviceEndVoltage,
78
+ utilizationPercent,
79
+ status,
80
+ };
81
+ }
@@ -0,0 +1,15 @@
1
+ ---
2
+ import { SEORenderer } from '@jjlmoya/utils-shared';
3
+ import { usbPowerBudgetCalculator } from './index';
4
+ import type { KnownLocale } from '../../types';
5
+
6
+ interface Props {
7
+ locale?: KnownLocale;
8
+ }
9
+
10
+ const { locale = 'en' } = Astro.props;
11
+ const content = await usbPowerBudgetCalculator.i18n[locale]?.();
12
+ if (!content) return null;
13
+ ---
14
+
15
+ {content.seo?.length > 0 && <SEORenderer content={{ locale, sections: content.seo }} />}
@@ -0,0 +1,32 @@
1
+ export interface UsbPowerBudgetCalculatorUI extends Record<string, string> {
2
+ profileLabel: string;
3
+ metricUnits: string;
4
+ imperialUnits: string;
5
+ voltageLabel: string;
6
+ currentLabel: string;
7
+ cableLengthLabel: string;
8
+ wireGaugeLabel: string;
9
+ deviceLoadLabel: string;
10
+ devicesLabel: string;
11
+ headroomLabel: string;
12
+ sourcePower: string;
13
+ requiredPower: string;
14
+ cableDrop: string;
15
+ deviceVoltage: string;
16
+ headroom: string;
17
+ utilization: string;
18
+ safeStatus: string;
19
+ tightStatus: string;
20
+ overStatus: string;
21
+ safeAdvice: string;
22
+ tightAdvice: string;
23
+ overAdvice: string;
24
+ busLane: string;
25
+ loadLane: string;
26
+ cableLane: string;
27
+ boardEyebrow: string;
28
+ sourceSocket: string;
29
+ deviceSocket: string;
30
+ energyFlow: string;
31
+ reservedLabel: string;
32
+ }