homebridge-eufy-security 4.4.2-beta.2 → 4.4.2-beta.20
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/CHANGELOG.md +1 -1
- package/FUNDING.yml +1 -0
- package/README.md +14 -17
- package/dist/accessories/AutoSyncStationAccessory.js +26 -30
- package/dist/accessories/AutoSyncStationAccessory.js.map +1 -1
- package/dist/accessories/BaseAccessory.js +33 -46
- package/dist/accessories/BaseAccessory.js.map +1 -1
- package/dist/accessories/CameraAccessory.js +92 -167
- package/dist/accessories/CameraAccessory.js.map +1 -1
- package/dist/accessories/Device.js +11 -15
- package/dist/accessories/Device.js.map +1 -1
- package/dist/accessories/EntrySensorAccessory.js +7 -12
- package/dist/accessories/EntrySensorAccessory.js.map +1 -1
- package/dist/accessories/LockAccessory.js +20 -24
- package/dist/accessories/LockAccessory.js.map +1 -1
- package/dist/accessories/MotionSensorAccessory.js +7 -12
- package/dist/accessories/MotionSensorAccessory.js.map +1 -1
- package/dist/accessories/StationAccessory.js +40 -45
- package/dist/accessories/StationAccessory.js.map +1 -1
- package/dist/config.js +1 -4
- package/dist/config.js.map +1 -1
- package/dist/controller/LocalLivestreamManager.js +85 -89
- package/dist/controller/LocalLivestreamManager.js.map +1 -1
- package/dist/controller/recordingDelegate.js +116 -108
- package/dist/controller/recordingDelegate.js.map +1 -1
- package/dist/controller/snapshotDelegate.js +379 -0
- package/dist/controller/snapshotDelegate.js.map +1 -0
- package/dist/controller/streamingDelegate.js +202 -207
- package/dist/controller/streamingDelegate.js.map +1 -1
- package/dist/index.js +9 -7
- package/dist/index.js.map +1 -1
- package/dist/interfaces.js +1 -2
- package/dist/interfaces.js.map +1 -1
- package/dist/platform.js +145 -165
- package/dist/platform.js.map +1 -1
- package/dist/settings.js +26 -19
- package/dist/settings.js.map +1 -1
- package/dist/utils/Talkback.js +9 -13
- package/dist/utils/Talkback.js.map +1 -1
- package/dist/utils/configTypes.js +12 -7
- package/dist/utils/configTypes.js.map +1 -1
- package/dist/utils/ffmpeg.js +44 -52
- package/dist/utils/ffmpeg.js.map +1 -1
- package/dist/utils/interfaces.js +1 -5
- package/dist/utils/interfaces.js.map +1 -1
- package/dist/utils/utils.js +32 -134
- package/dist/utils/utils.js.map +1 -1
- package/dist/version.js +1 -4
- package/dist/version.js.map +1 -1
- package/eslint.config.mjs +25 -47
- package/homebridge-eufy-security.png +0 -0
- package/homebridge-ui/public/app.js +221 -0
- package/homebridge-ui/public/assets/devices/eufycam3pro_large.png +0 -0
- package/homebridge-ui/public/assets/devices/homebase3_large.png +0 -0
- package/homebridge-ui/public/assets/devices/homebasemini_large.jpg +0 -0
- package/homebridge-ui/public/assets/devices/indoorcamC210_large.png +0 -0
- package/homebridge-ui/public/assets/devices/indoorcamC220_large.png +0 -0
- package/homebridge-ui/public/assets/devices/indoorcamE30_large.png +0 -0
- package/homebridge-ui/public/assets/devices/solocamc35_large.png +0 -0
- package/homebridge-ui/public/assets/devices/solocame30_large.png +0 -0
- package/homebridge-ui/public/components/device-card.js +141 -0
- package/homebridge-ui/public/components/guard-modes.js +88 -0
- package/homebridge-ui/public/components/number-input.js +121 -0
- package/homebridge-ui/public/components/select.js +73 -0
- package/homebridge-ui/public/components/toggle.js +68 -0
- package/homebridge-ui/public/index.html +24 -14
- package/homebridge-ui/public/services/api.js +129 -0
- package/homebridge-ui/public/services/config.js +144 -0
- package/homebridge-ui/public/style.css +705 -0
- package/homebridge-ui/public/utils/countries.js +73 -0
- package/homebridge-ui/public/utils/device-images.js +69 -0
- package/homebridge-ui/public/utils/helpers.js +47 -0
- package/homebridge-ui/public/views/dashboard.js +211 -0
- package/homebridge-ui/public/views/device-detail.js +631 -0
- package/homebridge-ui/public/views/login.js +625 -0
- package/homebridge-ui/public/views/settings.js +389 -0
- package/homebridge-ui/public/views/unsupported-detail.js +216 -0
- package/homebridge-ui/server.js +567 -450
- package/package.json +19 -59
- package/dist/accessories/AutoSyncStationAccessory.d.ts +0 -42
- package/dist/accessories/AutoSyncStationAccessory.d.ts.map +0 -1
- package/dist/accessories/BaseAccessory.d.ts +0 -56
- package/dist/accessories/BaseAccessory.d.ts.map +0 -1
- package/dist/accessories/CameraAccessory.d.ts +0 -84
- package/dist/accessories/CameraAccessory.d.ts.map +0 -1
- package/dist/accessories/Device.d.ts +0 -19
- package/dist/accessories/Device.d.ts.map +0 -1
- package/dist/accessories/EntrySensorAccessory.d.ts +0 -25
- package/dist/accessories/EntrySensorAccessory.d.ts.map +0 -1
- package/dist/accessories/LockAccessory.d.ts +0 -45
- package/dist/accessories/LockAccessory.d.ts.map +0 -1
- package/dist/accessories/MotionSensorAccessory.d.ts +0 -25
- package/dist/accessories/MotionSensorAccessory.d.ts.map +0 -1
- package/dist/accessories/StationAccessory.d.ts +0 -86
- package/dist/accessories/StationAccessory.d.ts.map +0 -1
- package/dist/config.d.ts +0 -31
- package/dist/config.d.ts.map +0 -1
- package/dist/controller/LocalLivestreamManager.d.ts +0 -31
- package/dist/controller/LocalLivestreamManager.d.ts.map +0 -1
- package/dist/controller/SnapshotManager.d.ts +0 -64
- package/dist/controller/SnapshotManager.d.ts.map +0 -1
- package/dist/controller/SnapshotManager.js +0 -434
- package/dist/controller/SnapshotManager.js.map +0 -1
- package/dist/controller/recordingDelegate.d.ts +0 -27
- package/dist/controller/recordingDelegate.d.ts.map +0 -1
- package/dist/controller/streamingDelegate.d.ts +0 -52
- package/dist/controller/streamingDelegate.d.ts.map +0 -1
- package/dist/index.d.ts +0 -7
- package/dist/index.d.ts.map +0 -1
- package/dist/interfaces.d.ts +0 -15
- package/dist/interfaces.d.ts.map +0 -1
- package/dist/platform.d.ts +0 -94
- package/dist/platform.d.ts.map +0 -1
- package/dist/settings.d.ts +0 -23
- package/dist/settings.d.ts.map +0 -1
- package/dist/utils/Talkback.d.ts +0 -21
- package/dist/utils/Talkback.d.ts.map +0 -1
- package/dist/utils/configTypes.d.ts +0 -68
- package/dist/utils/configTypes.d.ts.map +0 -1
- package/dist/utils/ffmpeg.d.ts +0 -111
- package/dist/utils/ffmpeg.d.ts.map +0 -1
- package/dist/utils/interfaces.d.ts +0 -8
- package/dist/utils/interfaces.d.ts.map +0 -1
- package/dist/utils/utils.d.ts +0 -35
- package/dist/utils/utils.d.ts.map +0 -1
- package/dist/version.d.ts +0 -2
- package/dist/version.d.ts.map +0 -1
- package/homebridge-ui/configui/app/util/types.d.ts +0 -65
- package/homebridge-ui/configui/app/util/types.d.ts.map +0 -1
- package/homebridge-ui/configui/app/util/types.js +0 -18
- package/homebridge-ui/configui/app/util/types.js.map +0 -1
- package/homebridge-ui/public/3rdpartylicenses.txt +0 -561
- package/homebridge-ui/public/assets/devices/homebase2_large.jpg +0 -0
- package/homebridge-ui/public/assets/devices/homebase3_large.jpg +0 -0
- package/homebridge-ui/public/assets/images/homebridge-eufy-security.png +0 -0
- package/homebridge-ui/public/favicon.ico +0 -0
- package/homebridge-ui/public/main.f6423287e0411c87.js +0 -1
- package/homebridge-ui/public/polyfills.efa2f09ba3e49167.js +0 -1
- package/homebridge-ui/public/runtime.ccde331d62c423b3.js +0 -1
- package/homebridge-ui/public/styles.13e635bf7a488639.css +0 -5
- package/homebridge-ui/server.d.ts +0 -2
- package/homebridge-ui/server.d.ts.map +0 -1
- package/homebridge-ui/server.js.map +0 -1
- package/homebridge-ui/version.d.ts +0 -2
- package/homebridge-ui/version.d.ts.map +0 -1
- package/homebridge-ui/version.js +0 -5
- package/homebridge-ui/version.js.map +0 -1
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Country list for login form dropdown.
|
|
3
|
+
* Ported from src/configui/app/countries.ts
|
|
4
|
+
*/
|
|
5
|
+
// eslint-disable-next-line no-unused-vars
|
|
6
|
+
const COUNTRIES = {
|
|
7
|
+
AF: 'Afghanistan', AX: 'Aland Islands', AL: 'Albania', DZ: 'Algeria',
|
|
8
|
+
AS: 'American Samoa', AD: 'Andorra', AO: 'Angola', AI: 'Anguilla',
|
|
9
|
+
AQ: 'Antarctica', AG: 'Antigua and Barbuda', AR: 'Argentina', AM: 'Armenia',
|
|
10
|
+
AW: 'Aruba', AU: 'Australia', AT: 'Austria', AZ: 'Azerbaijan',
|
|
11
|
+
BS: 'Bahamas', BH: 'Bahrain', BD: 'Bangladesh', BB: 'Barbados',
|
|
12
|
+
BY: 'Belarus', BE: 'Belgium', BZ: 'Belize', BJ: 'Benin', BM: 'Bermuda',
|
|
13
|
+
BT: 'Bhutan', BO: 'Bolivia', BA: 'Bosnia and Herzegovina', BW: 'Botswana',
|
|
14
|
+
BR: 'Brazil', BN: 'Brunei Darussalam', BG: 'Bulgaria', BF: 'Burkina Faso',
|
|
15
|
+
BI: 'Burundi', KH: 'Cambodia', CM: 'Cameroon', CA: 'Canada',
|
|
16
|
+
CV: 'Cape Verde', KY: 'Cayman Islands', CF: 'Central African Republic',
|
|
17
|
+
TD: 'Chad', CL: 'Chile', CN: 'China', CO: 'Colombia', KM: 'Comoros',
|
|
18
|
+
CG: 'Congo', CD: 'Congo, the Democratic Republic of the', CK: 'Cook Islands',
|
|
19
|
+
CR: 'Costa Rica', CI: "Cote D'Ivoire", HR: 'Croatia', CU: 'Cuba',
|
|
20
|
+
CW: 'Curacao', CY: 'Cyprus', CZ: 'Czech Republic', DK: 'Denmark',
|
|
21
|
+
DJ: 'Djibouti', DM: 'Dominica', DO: 'Dominican Republic', EC: 'Ecuador',
|
|
22
|
+
EG: 'Egypt', SV: 'El Salvador', GQ: 'Equatorial Guinea', ER: 'Eritrea',
|
|
23
|
+
EE: 'Estonia', ET: 'Ethiopia', FK: 'Falkland Islands (Malvinas)',
|
|
24
|
+
FO: 'Faroe Islands', FJ: 'Fiji', FI: 'Finland', FR: 'France',
|
|
25
|
+
GF: 'French Guiana', PF: 'French Polynesia', GA: 'Gabon', GM: 'Gambia',
|
|
26
|
+
GE: 'Georgia', DE: 'Germany', GH: 'Ghana', GI: 'Gibraltar', GR: 'Greece',
|
|
27
|
+
GL: 'Greenland', GD: 'Grenada', GP: 'Guadeloupe', GU: 'Guam',
|
|
28
|
+
GT: 'Guatemala', GG: 'Guernsey', GN: 'Guinea', GW: 'Guinea-Bissau',
|
|
29
|
+
GY: 'Guyana', HT: 'Haiti', VA: 'Holy See (Vatican City State)',
|
|
30
|
+
HN: 'Honduras', HK: 'Hong Kong', HU: 'Hungary', IS: 'Iceland', IN: 'India',
|
|
31
|
+
ID: 'Indonesia', IR: 'Iran, Islamic Republic of', IQ: 'Iraq', IE: 'Ireland',
|
|
32
|
+
IM: 'Isle of Man', IL: 'Israel', IT: 'Italy', JM: 'Jamaica', JP: 'Japan',
|
|
33
|
+
JE: 'Jersey', JO: 'Jordan', KZ: 'Kazakhstan', KE: 'Kenya', KI: 'Kiribati',
|
|
34
|
+
KP: "Korea, Democratic People's Republic of", KR: 'Korea, Republic of',
|
|
35
|
+
XK: 'Kosovo', KW: 'Kuwait', KG: 'Kyrgyzstan',
|
|
36
|
+
LA: "Lao People's Democratic Republic", LV: 'Latvia', LB: 'Lebanon',
|
|
37
|
+
LS: 'Lesotho', LR: 'Liberia', LY: 'Libyan Arab Jamahiriya',
|
|
38
|
+
LI: 'Liechtenstein', LT: 'Lithuania', LU: 'Luxembourg', MO: 'Macao',
|
|
39
|
+
MK: 'Macedonia, the Former Yugoslav Republic of', MG: 'Madagascar',
|
|
40
|
+
MW: 'Malawi', MY: 'Malaysia', MV: 'Maldives', ML: 'Mali', MT: 'Malta',
|
|
41
|
+
MH: 'Marshall Islands', MQ: 'Martinique', MR: 'Mauritania', MU: 'Mauritius',
|
|
42
|
+
YT: 'Mayotte', MX: 'Mexico', FM: 'Micronesia, Federated States of',
|
|
43
|
+
MD: 'Moldova, Republic of', MC: 'Monaco', MN: 'Mongolia', ME: 'Montenegro',
|
|
44
|
+
MS: 'Montserrat', MA: 'Morocco', MZ: 'Mozambique', MM: 'Myanmar',
|
|
45
|
+
NA: 'Namibia', NR: 'Nauru', NP: 'Nepal', NL: 'Netherlands',
|
|
46
|
+
NC: 'New Caledonia', NZ: 'New Zealand', NI: 'Nicaragua', NE: 'Niger',
|
|
47
|
+
NG: 'Nigeria', NU: 'Niue', NF: 'Norfolk Island',
|
|
48
|
+
MP: 'Northern Mariana Islands', NO: 'Norway', OM: 'Oman', PK: 'Pakistan',
|
|
49
|
+
PW: 'Palau', PS: 'Palestinian Territory, Occupied', PA: 'Panama',
|
|
50
|
+
PG: 'Papua New Guinea', PY: 'Paraguay', PE: 'Peru', PH: 'Philippines',
|
|
51
|
+
PN: 'Pitcairn', PL: 'Poland', PT: 'Portugal', PR: 'Puerto Rico',
|
|
52
|
+
QA: 'Qatar', RE: 'Reunion', RO: 'Romania', RU: 'Russian Federation',
|
|
53
|
+
RW: 'Rwanda', BL: 'Saint Barthelemy', SH: 'Saint Helena',
|
|
54
|
+
KN: 'Saint Kitts and Nevis', LC: 'Saint Lucia', MF: 'Saint Martin',
|
|
55
|
+
PM: 'Saint Pierre and Miquelon', VC: 'Saint Vincent and the Grenadines',
|
|
56
|
+
WS: 'Samoa', SM: 'San Marino', ST: 'Sao Tome and Principe',
|
|
57
|
+
SA: 'Saudi Arabia', SN: 'Senegal', RS: 'Serbia', SC: 'Seychelles',
|
|
58
|
+
SL: 'Sierra Leone', SG: 'Singapore', SX: 'Sint Maarten', SK: 'Slovakia',
|
|
59
|
+
SI: 'Slovenia', SB: 'Solomon Islands', SO: 'Somalia', ZA: 'South Africa',
|
|
60
|
+
SS: 'South Sudan', ES: 'Spain', LK: 'Sri Lanka', SD: 'Sudan',
|
|
61
|
+
SR: 'Suriname', SZ: 'Swaziland', SE: 'Sweden', CH: 'Switzerland',
|
|
62
|
+
SY: 'Syrian Arab Republic', TW: 'Taiwan, Province of China',
|
|
63
|
+
TJ: 'Tajikistan', TZ: 'Tanzania, United Republic of', TH: 'Thailand',
|
|
64
|
+
TL: 'Timor-Leste', TG: 'Togo', TK: 'Tokelau', TO: 'Tonga',
|
|
65
|
+
TT: 'Trinidad and Tobago', TN: 'Tunisia', TR: 'Turkey',
|
|
66
|
+
TM: 'Turkmenistan', TC: 'Turks and Caicos Islands', TV: 'Tuvalu',
|
|
67
|
+
UG: 'Uganda', UA: 'Ukraine', AE: 'United Arab Emirates',
|
|
68
|
+
GB: 'United Kingdom', US: 'United States', UY: 'Uruguay', UZ: 'Uzbekistan',
|
|
69
|
+
VU: 'Vanuatu', VE: 'Venezuela', VN: 'Viet Nam',
|
|
70
|
+
VG: 'Virgin Islands, British', VI: 'Virgin Islands, U.S.',
|
|
71
|
+
WF: 'Wallis and Futuna', EH: 'Western Sahara', YE: 'Yemen',
|
|
72
|
+
ZM: 'Zambia', ZW: 'Zimbabwe',
|
|
73
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Device type → image filename mapping.
|
|
3
|
+
* Ported from src/configui/app/util/deviceToImagesMap.ts
|
|
4
|
+
*/
|
|
5
|
+
// eslint-disable-next-line no-unused-vars
|
|
6
|
+
const DeviceImages = {
|
|
7
|
+
getImage(deviceType) {
|
|
8
|
+
switch (deviceType) {
|
|
9
|
+
case 0: return 'homebase2_large.png';
|
|
10
|
+
case 1: case 4: return 'eufycam_large.jpg';
|
|
11
|
+
case 2: return 'sensor_large.png';
|
|
12
|
+
case 3: return 'floodlight_large.jpg';
|
|
13
|
+
case 5: case 7: case 16: case 91: return 'batterydoorbell2k_large.png';
|
|
14
|
+
case 8: return 'eufycam2c_large.jpg';
|
|
15
|
+
case 9: case 14: case 15: return 'eufycam2_large.png';
|
|
16
|
+
case 10: return 'motionsensor_large.png';
|
|
17
|
+
case 11: return 'keypad_large.png';
|
|
18
|
+
case 18: return 'homebase3_large.png';
|
|
19
|
+
case 19: return 'eufycam3_large.jpg';
|
|
20
|
+
case 23: return 'eufycam3c_large.jpg';
|
|
21
|
+
case 24: return 'eufycame330_large.jpg';
|
|
22
|
+
case 25: return 'minibase_chime_T8023_large.jpg';
|
|
23
|
+
case 26: return 'eufycam3pro_large.png';
|
|
24
|
+
case 28: return 'homebasemini_large.jpg';
|
|
25
|
+
case 30: return 'indoorcamc120_large.png';
|
|
26
|
+
case 31: case 35: return 'indoorcamp24_large.png';
|
|
27
|
+
case 32: case 33: return 'solocame20_large.jpg';
|
|
28
|
+
case 34: return 'soloindoorcamc24_large.jpg';
|
|
29
|
+
case 37: case 39: return 'floodlight_large.jpg';
|
|
30
|
+
case 38: return 'floodlight2pro_large.jpg';
|
|
31
|
+
case 44: case 46: return 'solooutdoorcamc22_large.png';
|
|
32
|
+
case 45: return 'solooutdoorcamc24_large.jpg';
|
|
33
|
+
case 47: return 'floodlightcame340_large.jpg';
|
|
34
|
+
case 48: return 'solocams340_large.png';
|
|
35
|
+
case 50: case 51: case 52: case 53: case 54: case 55:
|
|
36
|
+
case 56: case 57: case 58: case 180: case 184:
|
|
37
|
+
return 'smartlock_t8500_large.jpg';
|
|
38
|
+
case 60: case 61: case 62: return 'solooutdoorcamc24_large.jpg';
|
|
39
|
+
case 63: return 'solocams220_large.jpg';
|
|
40
|
+
case 64: return 'solocamc210_large.jpg';
|
|
41
|
+
case 88: return 'solocame30_large.png';
|
|
42
|
+
case 90: return 'smartdrop_t8790_large.jpg';
|
|
43
|
+
case 93: return 'wireddoorbelldual_large.jpg';
|
|
44
|
+
case 94: return 'batterydoorbell_e340_large.jpg';
|
|
45
|
+
case 100: return 'indoorcammini_large.jpg';
|
|
46
|
+
case 104: return 'indoorcams350_large.jpg';
|
|
47
|
+
case 105: return 'indoorcamE30_large.png';
|
|
48
|
+
case 101: case 102: case 110:
|
|
49
|
+
case 131: case 132: case 133:
|
|
50
|
+
return 'garage_camera_t8452_large.jpg';
|
|
51
|
+
case 140: return 'smartsafe_s10_t7400_large.jpg';
|
|
52
|
+
case 141: return 'smartsafe_s12_t7401_large.jpg';
|
|
53
|
+
case 142: case 143: return 'smartsafe_s10_t7400_large.jpg';
|
|
54
|
+
case 151: return 'walllight_s100_large.jpg';
|
|
55
|
+
case 157: return 'smarttrack_link_t87B0_large.jpg';
|
|
56
|
+
case 159: return 'smarttrack_card_t87B2_large.jpg';
|
|
57
|
+
case 10005: return 'walllight_s120_large.jpg';
|
|
58
|
+
case 10008: return 'indoorcamC220_large.png';
|
|
59
|
+
case 10009: return 'indoorcamC210_large.png';
|
|
60
|
+
case 10035: return 'solocamc35_large.png';
|
|
61
|
+
default: return 'unknown.png';
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
/** Returns full relative path to device image */
|
|
66
|
+
getPath(deviceType) {
|
|
67
|
+
return 'assets/devices/' + this.getImage(deviceType);
|
|
68
|
+
},
|
|
69
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared utility functions used across views and components.
|
|
3
|
+
*/
|
|
4
|
+
// eslint-disable-next-line no-unused-vars
|
|
5
|
+
const Helpers = {
|
|
6
|
+
/**
|
|
7
|
+
* Escape HTML to prevent XSS when inserting user-supplied strings.
|
|
8
|
+
* @param {string} str
|
|
9
|
+
* @returns {string}
|
|
10
|
+
*/
|
|
11
|
+
escHtml(str) {
|
|
12
|
+
const div = document.createElement('div');
|
|
13
|
+
div.textContent = str;
|
|
14
|
+
return div.innerHTML;
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Append Node.js version warning text nodes to the given parent element.
|
|
19
|
+
* Uses DOM APIs exclusively — no innerHTML — to satisfy static-analysis security rules.
|
|
20
|
+
* @param {HTMLElement} parent
|
|
21
|
+
*/
|
|
22
|
+
appendNodeVersionWarning(parent) {
|
|
23
|
+
const parts = [
|
|
24
|
+
['text', 'Your Node.js version has removed '],
|
|
25
|
+
['code', 'RSA_PKCS1_PADDING'],
|
|
26
|
+
['text', ' support, which breaks streaming features. The rest of the plugin works fine. Use '],
|
|
27
|
+
['strong', 'Node.js v20.11.0'],
|
|
28
|
+
['text', ' or earlier, or upgrade to '],
|
|
29
|
+
['strong', 'v24.5.0+'],
|
|
30
|
+
['text', '. '],
|
|
31
|
+
];
|
|
32
|
+
for (const [type, value] of parts) {
|
|
33
|
+
if (type === 'text') {
|
|
34
|
+
parent.appendChild(document.createTextNode(value));
|
|
35
|
+
} else {
|
|
36
|
+
const el = document.createElement(type);
|
|
37
|
+
el.textContent = value;
|
|
38
|
+
parent.appendChild(el);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const link = document.createElement('a');
|
|
42
|
+
link.href = 'https://github.com/homebridge-plugins/homebridge-eufy-security/wiki/Node.js-Compatibility-with-Eufy-Security-Plugin';
|
|
43
|
+
link.target = '_blank';
|
|
44
|
+
link.textContent = 'Learn more';
|
|
45
|
+
parent.appendChild(link);
|
|
46
|
+
},
|
|
47
|
+
};
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dashboard View — displays device/station grid.
|
|
3
|
+
* Main screen after login. Shows all discovered devices as cards.
|
|
4
|
+
*/
|
|
5
|
+
// eslint-disable-next-line no-unused-vars
|
|
6
|
+
const DashboardView = {
|
|
7
|
+
|
|
8
|
+
_container: null,
|
|
9
|
+
|
|
10
|
+
async render(container) {
|
|
11
|
+
this._container = container;
|
|
12
|
+
container.innerHTML = '';
|
|
13
|
+
|
|
14
|
+
const config = await Config.get();
|
|
15
|
+
|
|
16
|
+
// Header
|
|
17
|
+
const header = document.createElement('div');
|
|
18
|
+
header.className = 'eufy-header';
|
|
19
|
+
|
|
20
|
+
const titleEl = document.createElement('h4');
|
|
21
|
+
titleEl.textContent = 'Eufy Security';
|
|
22
|
+
header.appendChild(titleEl);
|
|
23
|
+
|
|
24
|
+
const headerRight = document.createElement('div');
|
|
25
|
+
headerRight.className = 'eufy-header__right';
|
|
26
|
+
|
|
27
|
+
// Cache date label
|
|
28
|
+
if (App.state.cacheDate) {
|
|
29
|
+
const cacheLabel = document.createElement('span');
|
|
30
|
+
cacheLabel.className = 'eufy-cache-date';
|
|
31
|
+
const d = new Date(App.state.cacheDate);
|
|
32
|
+
cacheLabel.textContent = 'Cached: ' + d.toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' });
|
|
33
|
+
cacheLabel.title = 'Last fetched: ' + d.toLocaleString();
|
|
34
|
+
headerRight.appendChild(cacheLabel);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const btnGroup = document.createElement('div');
|
|
38
|
+
btnGroup.className = 'btn-group';
|
|
39
|
+
|
|
40
|
+
const btnRefresh = this._headerBtn('🔄', 'Re-login / Re-fetch', () => App.navigate('login'));
|
|
41
|
+
const btnSettings = this._headerBtn('⚙️', 'Settings', () => App.navigate('settings'));
|
|
42
|
+
|
|
43
|
+
btnGroup.appendChild(btnRefresh);
|
|
44
|
+
btnGroup.appendChild(btnSettings);
|
|
45
|
+
headerRight.appendChild(btnGroup);
|
|
46
|
+
header.appendChild(headerRight);
|
|
47
|
+
container.appendChild(header);
|
|
48
|
+
|
|
49
|
+
// Node.js version warning banner (shown if affected)
|
|
50
|
+
const warning = App.state.nodeVersionWarning;
|
|
51
|
+
if (warning && warning.affected) {
|
|
52
|
+
const banner = document.createElement('div');
|
|
53
|
+
banner.className = 'node-version-banner';
|
|
54
|
+
|
|
55
|
+
const iconDiv = document.createElement('div');
|
|
56
|
+
iconDiv.className = 'node-version-banner__icon';
|
|
57
|
+
iconDiv.textContent = '⚠️';
|
|
58
|
+
banner.appendChild(iconDiv);
|
|
59
|
+
|
|
60
|
+
const contentDiv = document.createElement('div');
|
|
61
|
+
contentDiv.className = 'node-version-banner__content';
|
|
62
|
+
|
|
63
|
+
const title = document.createElement('strong');
|
|
64
|
+
title.textContent = `Node.js ${warning.nodeVersion} — Streaming Incompatible`;
|
|
65
|
+
contentDiv.appendChild(title);
|
|
66
|
+
|
|
67
|
+
const textDiv = document.createElement('div');
|
|
68
|
+
textDiv.className = 'node-version-banner__text';
|
|
69
|
+
Helpers.appendNodeVersionWarning(textDiv);
|
|
70
|
+
contentDiv.appendChild(textDiv);
|
|
71
|
+
|
|
72
|
+
banner.appendChild(contentDiv);
|
|
73
|
+
container.appendChild(banner);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Device grid
|
|
77
|
+
const stations = App.state.stations || [];
|
|
78
|
+
|
|
79
|
+
if (stations.length === 0) {
|
|
80
|
+
container.insertAdjacentHTML('beforeend', `
|
|
81
|
+
<div class="text-center text-muted py-5">
|
|
82
|
+
<div style="font-size: 2rem; margin-bottom: 12px;">📦</div>
|
|
83
|
+
<p>No devices found. Try logging in again to discover your devices.</p>
|
|
84
|
+
<button class="btn btn-primary btn-sm mt-2" id="btn-go-login">Go to Login</button>
|
|
85
|
+
</div>
|
|
86
|
+
`);
|
|
87
|
+
container.querySelector('#btn-go-login').addEventListener('click', () => App.navigate('login'));
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const grid = document.createElement('div');
|
|
92
|
+
grid.className = 'row';
|
|
93
|
+
|
|
94
|
+
// Flatten: stations + their devices
|
|
95
|
+
const ignoreDevices = config.ignoreDevices || [];
|
|
96
|
+
const ignoreStations = config.ignoreStations || [];
|
|
97
|
+
|
|
98
|
+
// Separate supported and unsupported
|
|
99
|
+
const supported = [];
|
|
100
|
+
const unsupported = [];
|
|
101
|
+
|
|
102
|
+
stations.forEach((station) => {
|
|
103
|
+
// Check if station has a standalone device (device serial === station serial)
|
|
104
|
+
const standaloneDevice = (station.devices || []).find(
|
|
105
|
+
(d) => d.uniqueId === station.uniqueId
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
if (standaloneDevice) {
|
|
109
|
+
// Standalone: show as single card (the device IS the station)
|
|
110
|
+
const item = { ...standaloneDevice, type: standaloneDevice.type };
|
|
111
|
+
if (station.unsupported || item.unsupported) {
|
|
112
|
+
item.unsupported = true;
|
|
113
|
+
unsupported.push({ item, isStation: false, station });
|
|
114
|
+
} else {
|
|
115
|
+
supported.push({ item, isStation: false, station });
|
|
116
|
+
}
|
|
117
|
+
} else {
|
|
118
|
+
// Station card
|
|
119
|
+
if (!station.disabled) {
|
|
120
|
+
const stationItem = {
|
|
121
|
+
uniqueId: station.uniqueId,
|
|
122
|
+
displayName: station.displayName,
|
|
123
|
+
type: station.type,
|
|
124
|
+
typename: station.typename,
|
|
125
|
+
unsupported: station.unsupported,
|
|
126
|
+
ignored: station.ignored,
|
|
127
|
+
};
|
|
128
|
+
if (station.unsupported) {
|
|
129
|
+
unsupported.push({ item: stationItem, isStation: true, station });
|
|
130
|
+
} else {
|
|
131
|
+
supported.push({ item: stationItem, isStation: true, station });
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Device cards for this station
|
|
136
|
+
(station.devices || []).forEach((device) => {
|
|
137
|
+
if (station.unsupported || device.unsupported) {
|
|
138
|
+
unsupported.push({ item: { ...device, unsupported: true }, isStation: false, station });
|
|
139
|
+
} else {
|
|
140
|
+
supported.push({ item: device, isStation: false, station });
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// Render supported devices
|
|
147
|
+
supported.forEach(({ item, isStation }) => {
|
|
148
|
+
const isIgnored = isStation
|
|
149
|
+
? ignoreStations.includes(item.uniqueId)
|
|
150
|
+
: ignoreDevices.includes(item.uniqueId);
|
|
151
|
+
|
|
152
|
+
DeviceCard.render(grid, {
|
|
153
|
+
device: { ...item, ignored: isIgnored },
|
|
154
|
+
isStation: isStation,
|
|
155
|
+
enabled: !isIgnored,
|
|
156
|
+
onClick: (d) => {
|
|
157
|
+
const type = isStation ? 'station' : 'device';
|
|
158
|
+
App.navigate('detail/' + type + '/' + d.uniqueId);
|
|
159
|
+
},
|
|
160
|
+
onToggle: (d, enabled) => {
|
|
161
|
+
const type = isStation ? 'station' : 'device';
|
|
162
|
+
Config.toggleIgnore(d.uniqueId, !enabled, type);
|
|
163
|
+
// Re-render to update visual state
|
|
164
|
+
this.render(this._container);
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
container.appendChild(grid);
|
|
170
|
+
|
|
171
|
+
// Unsupported section
|
|
172
|
+
if (unsupported.length > 0) {
|
|
173
|
+
const section = document.createElement('div');
|
|
174
|
+
section.className = 'mt-4';
|
|
175
|
+
section.innerHTML = `
|
|
176
|
+
<div class="detail-section__title">Unsupported Devices</div>
|
|
177
|
+
<p class="text-muted" style="font-size: 0.8rem;">
|
|
178
|
+
These devices were detected but are not yet fully supported by the eufy-security-client library.
|
|
179
|
+
<a href="https://github.com/homebridge-plugins/homebridge-eufy-security/issues" target="_blank">Request support on GitHub</a>
|
|
180
|
+
</p>
|
|
181
|
+
`;
|
|
182
|
+
|
|
183
|
+
const unsupportedGrid = document.createElement('div');
|
|
184
|
+
unsupportedGrid.className = 'row';
|
|
185
|
+
|
|
186
|
+
unsupported.forEach(({ item, isStation }) => {
|
|
187
|
+
DeviceCard.render(unsupportedGrid, {
|
|
188
|
+
device: { ...item, unsupported: true },
|
|
189
|
+
isStation: isStation,
|
|
190
|
+
enabled: false,
|
|
191
|
+
onClick: (d) => {
|
|
192
|
+
App.navigate('unsupported/' + d.uniqueId);
|
|
193
|
+
},
|
|
194
|
+
onToggle: () => {},
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
section.appendChild(unsupportedGrid);
|
|
199
|
+
container.appendChild(section);
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
|
|
203
|
+
_headerBtn(icon, label, onClick) {
|
|
204
|
+
const btn = document.createElement('button');
|
|
205
|
+
btn.className = 'btn btn-outline-secondary btn-sm';
|
|
206
|
+
btn.innerHTML = icon + ' <span class="d-none d-sm-inline">' + label + '</span>';
|
|
207
|
+
btn.title = label;
|
|
208
|
+
btn.addEventListener('click', onClick);
|
|
209
|
+
return btn;
|
|
210
|
+
},
|
|
211
|
+
};
|