homebridge-eufy-nvr-gateway 0.1.0 → 0.2.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 (49) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +112 -314
  3. package/config.schema.json +16 -16
  4. package/dist/discovery.d.ts +5 -2
  5. package/dist/discovery.d.ts.map +1 -1
  6. package/dist/discovery.js +7 -2
  7. package/dist/discovery.js.map +1 -1
  8. package/dist/gateway.d.ts +7 -0
  9. package/dist/gateway.d.ts.map +1 -1
  10. package/dist/gateway.js +48 -15
  11. package/dist/gateway.js.map +1 -1
  12. package/dist/homebridge-ui/public/index.html +56 -70
  13. package/dist/platform.d.ts +1 -2
  14. package/dist/platform.d.ts.map +1 -1
  15. package/dist/platform.js +2 -2
  16. package/dist/platform.js.map +1 -1
  17. package/package.json +5 -8
  18. package/dist/discovery.test.d.ts +0 -2
  19. package/dist/discovery.test.d.ts.map +0 -1
  20. package/dist/discovery.test.js +0 -138
  21. package/dist/discovery.test.js.map +0 -1
  22. package/dist/gateway.test.d.ts +0 -2
  23. package/dist/gateway.test.d.ts.map +0 -1
  24. package/dist/gateway.test.js +0 -228
  25. package/dist/gateway.test.js.map +0 -1
  26. package/dist/index.test.d.ts +0 -2
  27. package/dist/index.test.d.ts.map +0 -1
  28. package/dist/index.test.js +0 -14
  29. package/dist/index.test.js.map +0 -1
  30. package/dist/platform.gateway.test.d.ts +0 -2
  31. package/dist/platform.gateway.test.d.ts.map +0 -1
  32. package/dist/platform.gateway.test.js +0 -118
  33. package/dist/platform.gateway.test.js.map +0 -1
  34. package/dist/recordingDelegate.test.d.ts +0 -2
  35. package/dist/recordingDelegate.test.d.ts.map +0 -1
  36. package/dist/recordingDelegate.test.js +0 -118
  37. package/dist/recordingDelegate.test.js.map +0 -1
  38. package/dist/settings.test.d.ts +0 -2
  39. package/dist/settings.test.d.ts.map +0 -1
  40. package/dist/settings.test.js +0 -18
  41. package/dist/settings.test.js.map +0 -1
  42. package/dist/streamingDelegate.gateway.test.d.ts +0 -2
  43. package/dist/streamingDelegate.gateway.test.d.ts.map +0 -1
  44. package/dist/streamingDelegate.gateway.test.js +0 -120
  45. package/dist/streamingDelegate.gateway.test.js.map +0 -1
  46. package/dist/streamingDelegate.test.d.ts +0 -2
  47. package/dist/streamingDelegate.test.d.ts.map +0 -1
  48. package/dist/streamingDelegate.test.js +0 -111
  49. package/dist/streamingDelegate.test.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,29 @@ All notable changes to this project. Versions follow [Semantic Versioning](https
4
4
  This plugin is based on [Homebridge Camera FFmpeg](https://github.com/homebridge-plugins/homebridge-camera-ffmpeg)
5
5
  4.1.0; its own history is in [its changelog](https://github.com/homebridge-plugins/homebridge-camera-ffmpeg/blob/latest/CHANGELOG.md).
6
6
 
7
+ ## [0.2.0] - 2026-11-08
8
+
9
+ ### Changed
10
+
11
+ - **Homebridge uses the gateway's stream login, not its web page login.** The stream login (the
12
+ one your NVR uses) now also finds and wakes the cameras, so Homebridge no longer needs or stores
13
+ the gateway's admin login. This needs eufy NVR Gateway 0.6.0 or later; older gateways still work
14
+ with the web login, which the settings page asks for only then.
15
+ - The model shown in the Home app comes with each camera from the gateway, where the gateway
16
+ reports it.
17
+ - The settings page shows the eufy NVR Gateway logo, and its support page links to this plugin and
18
+ the gateway.
19
+ - The README only covers what a gateway user needs; Camera FFmpeg's documentation site is gone
20
+ from the repository.
21
+
22
+ ## [0.1.1] - 2026-11-08
23
+
24
+ ### Changed
25
+
26
+ - Published to npm: install it from the Homebridge UI's plugin search. Every release is published
27
+ by the repository's workflow, with npm provenance.
28
+ - The package no longer contains the compiled tests.
29
+
7
30
  ## [0.1.0] - 2026-11-08
8
31
 
9
32
  The first release: Camera FFmpeg for battery eufy cameras behind the
package/README.md CHANGED
@@ -1,358 +1,156 @@
1
- # Homebridge eufy NVR Gateway
1
+ # <img src="https://raw.githubusercontent.com/dbanky/homebridge-eufy-nvr-gateway/main/media/logo.png" alt="" width="48" height="48" align="absmiddle"> Homebridge eufy NVR Gateway
2
2
 
3
- A special version of [Homebridge Camera FFmpeg](https://github.com/homebridge-plugins/homebridge-camera-ffmpeg)
4
- for battery eufy cameras behind the [eufy-nvr-gateway](https://github.com/dbanky/eufy-nvr-gateway).
3
+ [![npm](https://badgen.net/npm/v/homebridge-eufy-nvr-gateway)](https://www.npmjs.com/package/homebridge-eufy-nvr-gateway)
4
+
5
+ HomeKit live view for battery-powered eufy cameras, through the
6
+ [eufy NVR Gateway](https://github.com/dbanky/eufy-nvr-gateway). Opening a camera in the Home app
7
+ wakes it; closing it lets the camera sleep again.
5
8
 
6
9
  > [!IMPORTANT]
7
- > **This plugin needs the [eufy-nvr-gateway](https://github.com/dbanky/eufy-nvr-gateway), which you
10
+ > **This plugin needs the [eufy NVR Gateway](https://github.com/dbanky/eufy-nvr-gateway), which you
8
11
  > run separately.** The plugin doesn't talk to eufy at all: the gateway logs in to your eufy account,
9
- > keeps each camera's stream and wakes the cameras, and the plugin only brings its streams to HomeKit.
10
- > Without a running gateway, the plugin has no cameras to show.
12
+ > keeps an always-on stream for each camera and wakes the cameras; the plugin brings those streams to
13
+ > HomeKit. Without a running gateway, the plugin has no cameras to show.
11
14
  >
12
- > Set up the gateway first (it is a Docker container, e.g. on a NAS; see its
15
+ > Set up the gateway first (a Docker container, e.g. on a NAS; see its
13
16
  > [setup in five steps](https://github.com/dbanky/eufy-nvr-gateway#setup-in-five-steps)), make sure
14
- > its web page shows your cameras, then install this plugin and enter the gateway's IP address on its
15
- > settings page.
17
+ > its web page shows your cameras, then install this plugin.
16
18
  >
17
19
  > eufy allows one login per account at a time: if you also keep homebridge-eufy (or the eufy app on
18
20
  > another device) logged in with the gateway's account, they log each other out. Give the gateway an
19
21
  > account of its own, with the cameras shared to it
20
22
  > ([how](https://github.com/dbanky/eufy-nvr-gateway#the-eufy-account-and-its-two-factor-login)).
21
23
 
22
- The gateway gives every camera an always-on RTSP stream without keeping it awake: while the camera
23
- sleeps, the stream shows its last picture. Watching that stream does not wake the camera, so plain
24
- Camera FFmpeg would only ever show the sleeping picture in the Home app. This version does what
25
- homebridge-eufy does:
24
+ ## What it does
26
25
 
27
26
  - **Live view wakes the camera.** When the Home app opens a camera, the plugin asks the gateway to
28
- wake it. The stream starts at once with the gateway's *Waking up* picture and switches to live
29
- video when the camera is awake (usually a few seconds).
30
- - **It stays awake while you watch.** While at least one Home app viewer is open, the plugin repeats
31
- the wake-up every 10 seconds, so the gateway's hold time never runs out.
32
- - **It sleeps when you're done.** After the last viewer leaves, the camera sleeps when the gateway's
33
- hold time ends (30 s by default), or at once with `sleepWhenStreamEnds`.
34
- - **Snapshots never wake it.** Tiles in the Home app show the gateway's snapshot: the last picture
35
- while asleep, the live picture while awake.
36
-
37
- Cameras without `gatewayCamera` work exactly as in Camera FFmpeg.
38
-
39
- ## Setup page
40
-
41
- In the Homebridge UI, open the plugin's settings and enter the gateway's IP address. The page finds
42
- the gateway's cameras by itself; there is nothing else to type unless the gateway has logins:
43
-
44
- - **Gateway web page login**: only if the gateway's page asks for one. The page opens it when needed.
45
- - **Stream login**: shown only when the gateway's streams need one.
46
-
47
- For each camera the page shows its name and video source (both follow from the gateway and can't be
48
- changed) and fills in the branding (eufy, the model from the eufy account, the serial number, the
49
- gateway's version). You choose:
50
-
51
- - **Add to HomeKit**: on for every camera at the first setup; cameras the gateway gets later start off.
52
- - **Sound**: on when the gateway's stream for that camera has sound (`"audio": true` on the gateway).
53
- - **Sleep as soon as live view closes**: see `sleepWhenStreamEnds` below.
54
- - **Experimental – WIP → Video codec**: *Automatic* by default, which uses `copy` (no conversion,
55
- almost no CPU) whenever the gateway's stream is H.264, and converts with `libx264` when it is H.265
56
- or not known yet. It is picked again at every Homebridge start, so it follows the gateway once that
57
- has learned a camera's format. Choosing `copy` or `libx264` fixes it.
58
-
59
- Press *Save* to store it. Cameras set up by hand in the JSON config (without `gatewayCamera`) are
60
- kept as they are.
61
-
62
- ## Configuration
63
-
64
- What the setup page writes, for reference or for editing the JSON config by hand:
65
-
66
- ```json
67
- {
68
- "platform": "EufyNvrGateway",
69
- "gateway": {
70
- "url": "http://192.168.1.10:8099"
71
- },
72
- "cameras": [
73
- { "name": "front-door", "gatewayCamera": "front-door", "videoConfig": { "audio": true } },
74
- { "name": "garden", "gatewayCamera": "garden" }
75
- ]
76
- }
77
- ```
78
-
79
- Platform settings, under `gateway`:
80
-
81
- - `url`: _(Required for gateway cameras)_ The gateway's web page, e.g. `http://192.168.1.10:8099`.
82
- - `username`, `password`: The web page's login, if the gateway has one (`"web"` in its config.json).
83
- - `rtspPort`: The gateway's RTSP port, if not `8554`.
84
- - `rtspUsername`, `rtspPassword`: The streams' login, if the gateway has one (`"rtsp"` in its config.json).
85
-
86
- Camera settings:
87
-
88
- - `gatewayCamera`: The camera's `name` in the gateway's config.json. With it, `videoConfig.source`
89
- can be left out: the camera streams from `rtsp://<gateway>:8554/<gatewayCamera>` over TCP.
90
- - `wakeOnStream`: Wake the camera for live view (default `true`).
91
- - `videoConfig.vcodec`: Left out, it is picked at every start from the gateway (`copy` for an H.264
92
- stream, `libx264` otherwise). `copy` sends the gateway's picture at its own size and bitrate.
93
- - Branding (`manufacturer`, `model`, `serialNumber`, `firmwareRevision`): left out, it is filled in
94
- from the gateway at startup.
95
- - `sleepWhenStreamEnds`: Let the camera sleep the moment the last viewer leaves, if HomeKit was what
96
- woke it (default `false`). Off, it sleeps when the gateway's hold time ends; that leaves a session
97
- started by motion alone, so an NVR recording the same camera keeps its recording.
98
-
99
- All other Camera FFmpeg settings work as usual. Notes for gateway cameras:
100
-
101
- - HomeKit only plays H.264, while the gateway may pass a camera's own H.265 through: such cameras
102
- need `libx264`, which *Automatic* picks for them.
103
- - The picture changes size when a camera wakes (the sleeping picture, then the live video, which some
104
- cameras start at 720p). With `libx264` the plugin fits every frame into the size HomeKit asked for,
105
- with black bars where the camera's aspect ratio differs; without that, FFmpeg would keep the size
106
- of the first frame for the whole stream. `copy` passes the size changes on as they are, which is
107
- safe with `"transcode": true` on the gateway (one fixed size).
108
- - `audio: true` needs `"audio": true` for the camera on the gateway too.
109
- - A camera's battery pays for every live view: each one wakes it, even below the gateway's
110
- `minBatteryPct`, just like the wake button on the gateway's web page.
27
+ wake it. The picture appears at once (the gateway's *Waking up* picture) and switches to live video
28
+ when the camera is awake, usually within a few seconds.
29
+ - **It stays awake while you watch**, however long, and **sleeps when you're done**: when the
30
+ gateway's hold time ends (30 seconds by default), or at once with *Sleep as soon as live view
31
+ closes*.
32
+ - **Tiles never wake a camera.** The pictures on the Home app's tiles come from the gateway: the last
33
+ picture while a camera sleeps, the live one while it is awake.
34
+ - **Nothing to type but the gateway's address.** The settings page finds the gateway's cameras and
35
+ fills in the rest: names, streams, sound, video format, and the model and serial number shown in
36
+ the Home app.
111
37
 
112
38
  ## Installation
113
39
 
114
- **Before you start:** the [eufy-nvr-gateway](https://github.com/dbanky/eufy-nvr-gateway) must be
115
- running and reachable from the Homebridge host (its web page on port `8099`, its streams on `8554`),
116
- with your cameras in its `config.json`. This plugin depends on it and does not install it.
40
+ The gateway must be running and reachable from the Homebridge host: its web page on port `8099`,
41
+ its streams on `8554`.
117
42
 
118
- The plugin is not on npm. Download `homebridge-eufy-nvr-gateway-<version>.tgz` from the
119
- [releases](https://github.com/dbanky/homebridge-eufy-nvr-gateway/releases), or build it yourself:
43
+ In the Homebridge UI, open *Plugins*, search for `homebridge-eufy-nvr-gateway` and install it. Or,
44
+ in the Homebridge terminal:
120
45
 
121
46
  ```sh
122
- npm ci && npm run build && npm pack # makes homebridge-eufy-nvr-gateway-<version>.tgz
123
- ```
124
-
125
- Copy the `.tgz` to the Homebridge host (for the Homebridge Docker image: into its `/homebridge`
126
- folder) and install it from the Homebridge terminal:
127
-
128
- ```sh
129
- npm install --save /homebridge/homebridge-eufy-nvr-gateway-0.1.0.tgz
130
- ```
131
-
132
- Then restart Homebridge and add the platform `EufyNvrGateway` (the plugin's settings page in the
133
- Homebridge UI shows all options). It can run next to Camera FFmpeg; move a camera over by deleting it
134
- there and adding it here. Its accessories are published like Camera FFmpeg's: add each camera in the
135
- Home app with the Homebridge setup code.
136
-
137
- ## Development
138
-
139
- ```sh
140
- npm ci
141
- npm test # vitest
142
- npm run build # tsc into dist/
143
- ```
144
-
145
- The gateway integration is in `src/gateway.ts` (the gateway's HTTP API and the per-camera wake keeper);
146
- `src/streamingDelegate.ts` calls it on stream start and stop and for snapshots, and `src/platform.ts`
147
- derives each gateway camera's source.
148
-
149
- ---
150
-
151
- The rest of this README is Camera FFmpeg's own documentation. Read `Camera-ffmpeg` there as
152
- `EufyNvrGateway`.
153
-
154
- # Homebridge Camera FFmpeg
155
-
156
- [![npm](https://badgen.net/npm/v/@homebridge-plugins/homebridge-camera-ffmpeg) ![npm](https://badgen.net/npm/dt/@homebridge-plugins/homebridge-camera-ffmpeg)](https://www.npmjs.com/package/@homebridge-plugins/homebridge-camera-ffmpeg) [![verified-by-homebridge](https://badgen.net/badge/homebridge/verified/purple)](https://github.com/homebridge/homebridge/wiki/Verified-Plugins) [![certified-hoobs-plugin](https://badgen.net/badge/HOOBS/certified/yellow)](https://plugins.hoobs.org/plugin/homebridge-camera-ffmpeg)
157
-
158
- [Homebridge](https://homebridge.io) Plugin Providing [FFmpeg](https://www.ffmpeg.org)-based Camera Support
159
-
160
- ## Installation
161
-
162
- This plugin is supported under both [Homebridge](https://homebridge.io) and [HOOBS](https://hoobs.org/). It is highly recommended that you use either [Homebridge Config UI X](https://www.npmjs.com/package/homebridge-config-ui-x) or the HOOBS UI to install and configure this plugin.
163
-
164
- ### Manual Installation
165
-
166
- 1. Install this plugin using: `sudo npm install -g homebridge-camera-ffmpeg --unsafe-perm`.
167
- 2. Edit `config.json` manually to add your cameras. See below for instructions on that.
168
-
169
- ## Tested configurations
170
-
171
- Other users have been sharing configurations that work for them on our GitHub site. You may want to [check that](https://homebridge-plugins.github.io/homebridge-camera-ffmpeg/configs/) to see if anyone else has gotten your model of camera working already, or [share](https://github.com/homebridge-plugins/homebridge-camera-ffmpeg/issues/new?assignees=&labels=tested+config&template=tested_config.md) a configuration setup that works for you.
172
-
173
- ## Manual Configuration
174
-
175
- ### Most Important Parameters
176
-
177
- - `platform`: _(Required)_ Must always be set to `Camera-ffmpeg`.
178
- - `name`: _(Required)_ Set the camera name for display in the Home app.
179
- - `source`: _(Required)_ FFmpeg options on where to find and how to decode your camera's video stream. The most basic form is `-i` followed by your camera's URL.
180
- - `stillImageSource`: If your camera also provides a URL for a still image, that can be defined here with the same syntax as `source`. If not set, the plugin will grab one frame from `source`.
181
-
182
- #### Config Example
183
-
184
- ```json
185
- {
186
- "platform": "Camera-ffmpeg",
187
- "cameras": [
188
- {
189
- "name": "Camera Name",
190
- "videoConfig": {
191
- "source": "-i rtsp://username:password@example.com:554",
192
- "stillImageSource": "-i http://example.com/still_image.jpg",
193
- "maxStreams": 2,
194
- "maxWidth": 1280,
195
- "maxHeight": 720,
196
- "maxFPS": 30
197
- }
198
- }
199
- ]
200
- }
47
+ npm install homebridge-eufy-nvr-gateway
201
48
  ```
202
49
 
203
- ### Optional Parameters
50
+ Each release is also attached as a `.tgz` file on the
51
+ [releases page](https://github.com/dbanky/homebridge-eufy-nvr-gateway/releases)
52
+ (`npm install --save /path/to/homebridge-eufy-nvr-gateway-<version>.tgz`).
204
53
 
205
- - `motion`: Exposes the motion sensor for this camera. This can be triggered with [dummy switches](https://homebridge-plugins.github.io/homebridge-camera-ffmpeg/automation/switch.html), [MQTT messages](https://homebridge-plugins.github.io/homebridge-camera-ffmpeg/automation/mqtt.html), or [via HTTP](https://homebridge-plugins.github.io/homebridge-camera-ffmpeg/automation/http.html), depending on what features are enabled in the config. (Default: `false`)
206
- - `doorbell`: Exposes the doorbell device for this camera. This can be triggered with [dummy switches](https://homebridge-plugins.github.io/homebridge-camera-ffmpeg/automation/switch.html), [MQTT messages](https://homebridge-plugins.github.io/homebridge-camera-ffmpeg/automation/mqtt.html), or [via HTTP](https://homebridge-plugins.github.io/homebridge-camera-ffmpeg/automation/http.html), depending on what features are enabled in the config. (Default: `false`)
207
- - `switches`: Enables dummy switches to trigger motion and/or doorbell, if either of those are enabled. When enabled there will be an additional switch that triggers the motion or doorbell event. See the project site for [more detailed instructions](https://homebridge-plugins.github.io/homebridge-camera-ffmpeg/automation/switch.html). (Default: `false`)
208
- - `motionTimeout`: The number of seconds after triggering to reset the motion sensor. Set to 0 to disable resetting of motion trigger for MQTT or HTTP. (Default: `1`)
209
- - `motionDoorbell`: Rings the doorbell when motion is activated. This allows for motion alerts to appear on Apple TVs. (Default: `false`)
210
- - `manufacturer`: Set the manufacturer name for display in the Home app. (Default: `Homebridge`)
211
- - `model`: Set the model for display in the Home app. (Default: `Camera FFmpeg`)
212
- - `serialNumber`: Set the serial number for display in the Home app. (Default: `SerialNumber`)
213
- - `firmwareRevision`: Set the firmware revision for display in the Home app. (Default: current plugin version)
214
- - `unbridge`: Bridged cameras can cause slowdowns of the entire Homebridge instance. If unbridged, the camera will need to be added to HomeKit manually. (Default: `true`)
54
+ ## Setup
215
55
 
216
- #### Config Example with Manufacturer and Model Set
56
+ Open the plugin's settings in the Homebridge UI and enter the gateway's IP address (its port too, if
57
+ it isn't `8099`). The page finds the gateway's cameras at once.
217
58
 
218
- ```json
219
- {
220
- "platform": "Camera-ffmpeg",
221
- "cameras": [
222
- {
223
- "name": "Camera Name",
224
- "manufacturer": "ACME, Inc.",
225
- "model": "ABC-123",
226
- "serialNumber": "1234567890",
227
- "firmwareRevision": "1.0",
228
- "videoConfig": {
229
- "source": "-i rtsp://username:password@example.com:554",
230
- "stillImageSource": "-i http://example.com/still_image.jpg",
231
- "maxStreams": 2,
232
- "maxWidth": 1280,
233
- "maxHeight": 720,
234
- "maxFPS": 30
235
- }
236
- }
237
- ]
238
- }
239
- ```
240
-
241
- ### Optional videoConfig Parameters
59
+ **Logins.** Only if the gateway has them:
242
60
 
243
- - `returnAudioTarget`: _(EXPERIMENTAL - WIP)_ The FFmpeg output command for directing audio back to a two-way capable camera. This feature is still in development and a configuration that works today may not work in the future.
244
- - `maxStreams`: The maximum number of streams that will be allowed at once to this camera. (Default: `2`)
245
- - `maxWidth`: The maximum width used for video streamed to HomeKit. If set to 0, the resolution of the source is used. If not set, will use any size HomeKit requests.
246
- - `maxHeight`: The maximum height used for video streamed to HomeKit. If set to 0, the resolution of the source is used. If not set, will use any size HomeKit requests.
247
- - `maxFPS`: The maximum frame rate used for video streamed to HomeKit. If set to 0, the framerate of the source is used. If not set, will use any frame rate HomeKit requests.
248
- - `maxBitrate`: The maximum bitrate used for video streamed to HomeKit, in kbit/s. If not set, will use any bitrate HomeKit requests.
249
- - `forceMax`: If set, the settings requested by HomeKit will be overridden with any 'maximum' values defined in this config. (Default: `false`)
250
- - `vcodec`: Set the codec used for encoding video sent to HomeKit, must be H.264-based. You can change to a hardware accelerated video codec with this option, if one is available. (Default: `libx264`)
251
- - `audio`: Enables audio streaming from camera. (Default: `false`)
252
- - `packetSize`: If audio or video is choppy try a smaller value, should be set to a multiple of 188. (Default: `1316`)
253
- - `mapvideo`: Selects the stream used for video. (Default: FFmpeg [automatically selects](https://ffmpeg.org/ffmpeg.html#Automatic-stream-selection) a video stream)
254
- - `mapaudio`: Selects the stream used for audio. (Default: FFmpeg [automatically selects](https://ffmpeg.org/ffmpeg.html#Automatic-stream-selection) an audio stream)
255
- - `videoFilter`: Comma-delimited list of additional video filters for FFmpeg to run on the video. If 'none' is included, the default video filters are disabled.
256
- - `encoderOptions`: Options to be passed to the video encoder. (Default: `-preset ultrafast -tune zerolatency` if using libx264)
257
- - `debug`: Includes debugging output from the main FFmpeg process in the Homebridge log. (Default: `false`)
258
- - `debugReturn`: Includes debugging output from the FFmpeg used for return audio in the Homebridge log. (Default: `false`)
61
+ - **Stream login**: the gateway's `"rtsp"` login, the one your NVR uses. The page asks for it when
62
+ the gateway needs a login. Homebridge plays the streams with it, and finds and wakes the cameras
63
+ with it.
64
+ - **Web page login**: only for gateways before 0.6.0, which don't accept the stream login for
65
+ finding and waking cameras. The page asks for it only then. Updating the gateway makes it unnecessary, and the
66
+ page doesn't store it when the stream login is enough.
259
67
 
260
- #### More Complicated Example
68
+ **For each camera** the page shows its name and stream (both follow from the gateway) and fills in
69
+ the model and serial number. You choose:
261
70
 
262
- ```json
263
- {
264
- "platform": "Camera-ffmpeg",
265
- "cameras": [
266
- {
267
- "name": "Camera Name",
268
- "videoConfig": {
269
- "source": "-i rtsp://myfancy_rtsp_stream",
270
- "stillImageSource": "-i http://faster_still_image_grab_url/this_is_optional.jpg",
271
- "maxStreams": 2,
272
- "maxWidth": 1280,
273
- "maxHeight": 720,
274
- "maxFPS": 30,
275
- "maxBitrate": 200,
276
- "vcodec": "h264_omx",
277
- "audio": false,
278
- "packetSize": 188,
279
- "hflip": true,
280
- "additionalCommandline": "-x264-params intra-refresh=1:bframes=0",
281
- "debug": true
282
- }
283
- }
284
- ]
285
- }
286
- ```
287
-
288
- ### Camera MQTT Parameters
289
-
290
- - `motionTopic`: The MQTT topic to watch for motion alerts.
291
- - `motionMessage`: The message to watch for to trigger motion alerts. Will use the name of the camera if blank.
292
- - `motionResetTopic`: The MQTT topic to watch for motion resets.
293
- - `motionResetMessage`: The message to watch for to trigger motion resets. Will use the name of the camera if blank.
294
- - `doorbellTopic`: The MQTT topic to watch for doorbell alerts.
295
- - `doorbellMessage`: The message to watch for to trigger doorbell alerts. Will use the name of the camera if blank.
296
-
297
- #### Camera MQTT Example
71
+ - **Add to HomeKit**: every camera at the first setup; cameras the gateway gets later start off.
72
+ - **Sound**: on when the gateway's stream for that camera has sound (`"audio": true` on the gateway).
73
+ - **Sleep as soon as live view closes**: off, the camera sleeps when the gateway's hold time ends.
74
+ That leaves a session the camera started for motion alone, so an NVR recording the same camera
75
+ keeps its recording.
76
+ - **Experimental – WIP → Video codec**: *Automatic* passes the gateway's H.264 to HomeKit as it is
77
+ (`copy`, almost no CPU), and converts with `libx264` when the stream is H.265, which HomeKit can't
78
+ play, or when the gateway doesn't know the camera's format yet. It is decided again at every
79
+ Homebridge start.
80
+
81
+ Press *Save* and restart Homebridge. Then add each camera in the Home app (*Add Accessory*, *More
82
+ options…*) with the Homebridge setup code; the cameras are separate accessories.
83
+
84
+ ## Good to know
85
+
86
+ - **Battery.** Every live view wakes the camera, even below the gateway's `minBatteryPct`, just like
87
+ the wake button on the gateway's web page.
88
+ - **The first seconds of a live view** show the *Waking up* picture: a sleeping camera needs a few
89
+ seconds (some models longer) before it sends video.
90
+ - **Moving from homebridge-eufy or Camera FFmpeg.** Remove the cameras there, or they appear twice in
91
+ the Home app. Don't run homebridge-eufy with the gateway's eufy account (see above).
92
+ - **Too many wrong logins.** After 10 wrong logins from the Homebridge machine within 5 minutes, the
93
+ gateway refuses its logins for 5 minutes; the Homebridge log says so. Correct the login on the
94
+ settings page and wait.
95
+ - **No sound in the Home app** with an error about `libfdk_aac` in the log: the FFmpeg that comes with
96
+ the plugin wasn't downloaded. Run `npm rebuild ffmpeg-for-homebridge` in the Homebridge terminal.
97
+ - **Startup check.** At every start the log names each camera as found on the gateway, or says what
98
+ is wrong (address, login, or a camera name the gateway doesn't know).
99
+
100
+ ## Settings in config.json
101
+
102
+ What the settings page writes, for reference:
298
103
 
299
104
  ```json
300
105
  {
301
- "platform": "Camera-ffmpeg",
106
+ "platform": "EufyNvrGateway",
107
+ "name": "eufy NVR Gateway",
108
+ "gateway": {
109
+ "url": "http://192.168.1.10:8099",
110
+ "rtspUsername": "protect",
111
+ "rtspPassword": "choose-another-password"
112
+ },
302
113
  "cameras": [
303
114
  {
304
- "name": "Camera Name",
305
- "videoConfig": {
306
- "source": "-i rtsp://myfancy_rtsp_stream"
307
- },
308
- "mqtt": {
309
- "motionTopic": "home/camera",
310
- "motionMessage": "ON",
311
- "motionResetTopic": "home/camera",
312
- "motionResetMessage": "OFF",
313
- "doorbellTopic": "home/doobell",
314
- "doorbellMessage": "ON"
315
- }
115
+ "name": "front-door",
116
+ "gatewayCamera": "front-door",
117
+ "sleepWhenStreamEnds": false,
118
+ "videoConfig": { "audio": true }
316
119
  }
317
120
  ]
318
121
  }
319
122
  ```
320
123
 
321
- ### Automation Parameters
322
-
323
- - `mqtt`: Defines the hostname or IP of the MQTT broker to connect to for MQTT-based automation. If not set, MQTT support is not started. See the project site for [more information on using MQTT](https://homebridge-plugins.github.io/homebridge-camera-ffmpeg/automation/mqtt.html).
324
- - `portmqtt`: The port of the MQTT broker. (Default: `1883`)
325
- - `tlsmqtt`: Use TLS to connect to the MQTT broker. (Default: `false`)
326
- - `usermqtt`: The username used to connect to your MQTT broker. If not set, no authentication is used.
327
- - `passmqtt`: The password used to connect to your MQTT broker. If not set, no authentication is used.
328
- - `porthttp`: The port to listen on for HTTP-based automation. If not set, HTTP support is not started. See the project site for [more information on using HTTP](https://homebridge-plugins.github.io/homebridge-camera-ffmpeg/automation/http.html).
329
- - `localhttp`: Only allow HTTP calls from localhost. Useful if using helper plugins that translate to HTTP. (Default: `false`)
124
+ | Setting | Meaning |
125
+ |---|---|
126
+ | `gateway.url` | The gateway's web page |
127
+ | `gateway.rtspUsername`, `gateway.rtspPassword` | The gateway's stream login (`"rtsp"` in its config.json), if it has one |
128
+ | `gateway.rtspPort` | The gateway's RTSP port, if not `8554` |
129
+ | `gateway.username`, `gateway.password` | The gateway's web page login: only for gateways before 0.6.0, which refuse the stream login for finding and waking cameras |
130
+ | `cameras[].gatewayCamera` | The camera's `name` in the gateway's config.json |
131
+ | `cameras[].name` | The name in the Home app (the settings page uses the gateway's name) |
132
+ | `cameras[].wakeOnStream` | `false`: live view doesn't wake the camera (default `true`) |
133
+ | `cameras[].sleepWhenStreamEnds` | `true`: the camera sleeps as soon as the last viewer leaves, if live view woke it (default `false`) |
134
+ | `cameras[].videoConfig.audio` | Sound in the Home app |
135
+ | `cameras[].videoConfig.vcodec` | `copy` or `libx264`; left out, it is chosen at every start |
136
+
137
+ The model, serial number and firmware shown in the Home app (`manufacturer`, `model`,
138
+ `serialNumber`, `firmwareRevision`) are filled in from the gateway when left out.
330
139
 
331
- #### Automation Example
140
+ ## Development
332
141
 
333
- ```json
334
- {
335
- "platform": "Camera-ffmpeg",
336
- "mqtt": "127.0.0.1",
337
- "porthttp": "8080",
338
- "cameras": []
339
- }
142
+ ```sh
143
+ npm ci
144
+ npm test # vitest
145
+ npm run build # TypeScript into dist/
340
146
  ```
341
147
 
342
- ### Rarely Needed Parameters
343
-
344
- - `videoProcessor`: Defines which video processor is used to decode and encode videos, must take the same parameters as FFmpeg. Common uses would be `avconv` or the path to a custom-compiled version of FFmpeg. If not set, will use the included version of FFmpeg, or the version of FFmpeg installed on the system if no included version is available.
345
-
346
- #### Rare Option Example
347
-
348
- ```json
349
- {
350
- "platform": "Camera-ffmpeg",
351
- "videoProcessor": "/usr/bin/ffmpeg",
352
- "cameras": []
353
- }
354
- ```
148
+ The gateway integration is in `src/gateway.ts` (its API and keeping a camera awake) and
149
+ `src/discovery.ts` (what follows from the gateway); the settings page is `src/homebridge-ui/`.
150
+ Publishing a GitHub release publishes the package to npm (`.github/workflows/publish.yml`).
355
151
 
356
- ## Credit
152
+ ## License
357
153
 
358
- Homebridge Camera FFmpeg is based on code originally written by [Khaos Tian](https://twitter.com/khaost).
154
+ Apache License 2.0, see [LICENSE](LICENSE). Based on
155
+ [Homebridge Camera FFmpeg](https://github.com/homebridge-plugins/homebridge-camera-ffmpeg) by Khaos
156
+ Tian, Sunoo and the Homebridge team; the changes are listed in the [CHANGELOG](CHANGELOG.md).
@@ -4,8 +4,8 @@
4
4
  "singular": true,
5
5
  "customUi": true,
6
6
  "customUiPath": "./dist/homebridge-ui",
7
- "headerDisplay": "FFmpeg cameras for HomeKit, with wake-on-view for battery eufy cameras behind the [eufy-nvr-gateway](https://github.com/dbanky/eufy-nvr-gateway).",
8
- "footerDisplay": "Based on Homebridge Camera FFmpeg. Shared configurations for ordinary cameras: [the project site](https://homebridge-plugins.github.io/homebridge-camera-ffmpeg/configs/).",
7
+ "headerDisplay": "HomeKit live view for battery eufy cameras behind the [eufy NVR Gateway](https://github.com/dbanky/eufy-nvr-gateway), which must run separately.",
8
+ "footerDisplay": "Setup and settings: [README](https://github.com/dbanky/homebridge-eufy-nvr-gateway#readme).",
9
9
  "schema": {
10
10
  "name": {
11
11
  "title": "Name",
@@ -23,13 +23,13 @@
23
23
  "placeholder": "http://192.168.1.10:8099",
24
24
  "description": "The gateway's web page. Needed for cameras that have a Gateway Camera Name."
25
25
  },
26
- "username": {
27
- "title": "Web Login: Username",
26
+ "rtspUsername": {
27
+ "title": "Stream Login: Username",
28
28
  "type": "string",
29
- "description": "Only if the gateway's web page has a login (\"web\" in its config.json)."
29
+ "description": "The gateway's stream login (\"rtsp\" in its config.json), the one your NVR uses. Plays the streams, and finds and wakes the cameras."
30
30
  },
31
- "password": {
32
- "title": "Web Login: Password",
31
+ "rtspPassword": {
32
+ "title": "Stream Login: Password",
33
33
  "type": "string",
34
34
  "x-schema-form": {
35
35
  "type": "password"
@@ -41,13 +41,13 @@
41
41
  "placeholder": 8554,
42
42
  "description": "The gateway's RTSP port (\"ports.rtsp\" in its config.json)."
43
43
  },
44
- "rtspUsername": {
45
- "title": "Stream Login: Username",
44
+ "username": {
45
+ "title": "Web Page Login: Username (gateways before 0.6.0)",
46
46
  "type": "string",
47
- "description": "Only if the gateway's streams have a login (\"rtsp\" in its config.json)."
47
+ "description": "Only for gateways before 0.6.0, which don't accept the stream login for finding and waking cameras: the login of the gateway's web page (\"web\" in its config.json)."
48
48
  },
49
- "rtspPassword": {
50
- "title": "Stream Login: Password",
49
+ "password": {
50
+ "title": "Web Page Login: Password (gateways before 0.6.0)",
51
51
  "type": "string",
52
52
  "x-schema-form": {
53
53
  "type": "password"
@@ -371,11 +371,11 @@
371
371
  "expanded": true,
372
372
  "items": [
373
373
  "gateway.url",
374
- "gateway.username",
375
- "gateway.password",
376
- "gateway.rtspPort",
377
374
  "gateway.rtspUsername",
378
- "gateway.rtspPassword"
375
+ "gateway.rtspPassword",
376
+ "gateway.rtspPort",
377
+ "gateway.username",
378
+ "gateway.password"
379
379
  ]
380
380
  },
381
381
  {
@@ -4,6 +4,7 @@ import { GatewayClient } from './gateway.js';
4
4
  export declare const DEFAULT_WEB_PORT = 8099;
5
5
  export interface GatewayCameraStatus {
6
6
  name: string;
7
+ model?: string | null;
7
8
  serial: string;
8
9
  state: string;
9
10
  battery?: number | null;
@@ -25,6 +26,7 @@ export interface GatewayStatus {
25
26
  gateway?: {
26
27
  version?: string;
27
28
  login?: boolean;
29
+ streamLoginForApi?: boolean;
28
30
  };
29
31
  cameras?: GatewayCameraStatus[];
30
32
  }
@@ -61,12 +63,13 @@ export interface Discovery {
61
63
  version: string;
62
64
  rtspPort: number;
63
65
  rtspLogin: boolean;
66
+ apiLogin: 'stream' | 'web' | 'none';
64
67
  cameras: DiscoveredCamera[];
65
68
  }
66
69
  export declare function normalizeGatewayAddress(input: string): string;
67
70
  export declare function firmwareFrom(version?: string): string;
68
- export declare function brandingFor(camera: Pick<GatewayCameraStatus, 'serial'>, devices: GatewayDevice[], version?: string): Branding;
71
+ export declare function brandingFor(camera: Pick<GatewayCameraStatus, 'serial' | 'model'>, devices: GatewayDevice[], version?: string): Branding;
69
72
  export declare function codecFor(camera: Pick<GatewayCameraStatus, 'video'>): CodecChoice;
70
- export declare function discover(gateway: GatewayConfig, client?: Pick<GatewayClient, 'status' | 'devices'>): Promise<Discovery>;
73
+ export declare function discover(gateway: GatewayConfig, client?: Pick<GatewayClient, 'status' | 'devices' | 'usedLogin'>): Promise<Discovery>;
71
74
  export declare function cameraConfigFor(camera: DiscoveredCamera, previous?: CameraConfig): CameraConfig;
72
75
  //# sourceMappingURL=discovery.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../src/discovery.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../src/discovery.ts"],"names":[],"mappings":"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"}