pyezvizapi 1.0.2.1__tar.gz → 1.0.2.2__tar.gz
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.
- {pyezvizapi-1.0.2.1/pyezvizapi.egg-info → pyezvizapi-1.0.2.2}/PKG-INFO +1 -1
- {pyezvizapi-1.0.2.1 → pyezvizapi-1.0.2.2}/pyezvizapi/camera.py +71 -14
- {pyezvizapi-1.0.2.1 → pyezvizapi-1.0.2.2/pyezvizapi.egg-info}/PKG-INFO +1 -1
- {pyezvizapi-1.0.2.1 → pyezvizapi-1.0.2.2}/setup.py +1 -1
- {pyezvizapi-1.0.2.1 → pyezvizapi-1.0.2.2}/LICENSE +0 -0
- {pyezvizapi-1.0.2.1 → pyezvizapi-1.0.2.2}/LICENSE.md +0 -0
- {pyezvizapi-1.0.2.1 → pyezvizapi-1.0.2.2}/MANIFEST.in +0 -0
- {pyezvizapi-1.0.2.1 → pyezvizapi-1.0.2.2}/README.md +0 -0
- {pyezvizapi-1.0.2.1 → pyezvizapi-1.0.2.2}/pyezvizapi/__init__.py +0 -0
- {pyezvizapi-1.0.2.1 → pyezvizapi-1.0.2.2}/pyezvizapi/__main__.py +0 -0
- {pyezvizapi-1.0.2.1 → pyezvizapi-1.0.2.2}/pyezvizapi/api_endpoints.py +0 -0
- {pyezvizapi-1.0.2.1 → pyezvizapi-1.0.2.2}/pyezvizapi/cas.py +0 -0
- {pyezvizapi-1.0.2.1 → pyezvizapi-1.0.2.2}/pyezvizapi/client.py +0 -0
- {pyezvizapi-1.0.2.1 → pyezvizapi-1.0.2.2}/pyezvizapi/constants.py +0 -0
- {pyezvizapi-1.0.2.1 → pyezvizapi-1.0.2.2}/pyezvizapi/exceptions.py +0 -0
- {pyezvizapi-1.0.2.1 → pyezvizapi-1.0.2.2}/pyezvizapi/light_bulb.py +0 -0
- {pyezvizapi-1.0.2.1 → pyezvizapi-1.0.2.2}/pyezvizapi/models.py +0 -0
- {pyezvizapi-1.0.2.1 → pyezvizapi-1.0.2.2}/pyezvizapi/mqtt.py +0 -0
- {pyezvizapi-1.0.2.1 → pyezvizapi-1.0.2.2}/pyezvizapi/test_cam_rtsp.py +0 -0
- {pyezvizapi-1.0.2.1 → pyezvizapi-1.0.2.2}/pyezvizapi/test_mqtt.py +0 -0
- {pyezvizapi-1.0.2.1 → pyezvizapi-1.0.2.2}/pyezvizapi/utils.py +0 -0
- {pyezvizapi-1.0.2.1 → pyezvizapi-1.0.2.2}/pyezvizapi.egg-info/SOURCES.txt +0 -0
- {pyezvizapi-1.0.2.1 → pyezvizapi-1.0.2.2}/pyezvizapi.egg-info/dependency_links.txt +0 -0
- {pyezvizapi-1.0.2.1 → pyezvizapi-1.0.2.2}/pyezvizapi.egg-info/entry_points.txt +0 -0
- {pyezvizapi-1.0.2.1 → pyezvizapi-1.0.2.2}/pyezvizapi.egg-info/requires.txt +0 -0
- {pyezvizapi-1.0.2.1 → pyezvizapi-1.0.2.2}/pyezvizapi.egg-info/top_level.txt +0 -0
- {pyezvizapi-1.0.2.1 → pyezvizapi-1.0.2.2}/setup.cfg +0 -0
|
@@ -99,6 +99,7 @@ class EzvizCamera:
|
|
|
99
99
|
self._alarmmotiontrigger: dict[str, Any] = {
|
|
100
100
|
"alarm_trigger_active": False,
|
|
101
101
|
"timepassed": None,
|
|
102
|
+
"last_alarm_time_str": None,
|
|
102
103
|
}
|
|
103
104
|
self._record: EzvizDeviceRecord | None = None
|
|
104
105
|
|
|
@@ -171,15 +172,22 @@ class EzvizCamera:
|
|
|
171
172
|
|
|
172
173
|
Prefer numeric epoch fields if available to avoid parsing localized strings.
|
|
173
174
|
"""
|
|
174
|
-
# Use timezone-aware datetimes
|
|
175
|
+
# Use timezone-aware datetimes. Compute both camera-local and UTC "now".
|
|
175
176
|
tzinfo = self._get_tzinfo()
|
|
176
|
-
|
|
177
|
+
now_local = datetime.datetime.now(tz=tzinfo).replace(microsecond=0)
|
|
178
|
+
now_utc = datetime.datetime.now(tz=datetime.UTC).replace(microsecond=0)
|
|
177
179
|
|
|
178
180
|
# Prefer epoch fields if available
|
|
179
181
|
epoch = self._last_alarm.get("alarmStartTime") or self._last_alarm.get(
|
|
180
182
|
"alarmTime"
|
|
181
183
|
)
|
|
182
184
|
last_alarm_dt: datetime.datetime | None = None
|
|
185
|
+
# Capture string time if present so we can cross-check epoch skew
|
|
186
|
+
raw_time_str = str(
|
|
187
|
+
self._last_alarm.get("alarmStartTimeStr")
|
|
188
|
+
or self._last_alarm.get("alarmTimeStr")
|
|
189
|
+
or ""
|
|
190
|
+
)
|
|
183
191
|
if epoch is not None:
|
|
184
192
|
try:
|
|
185
193
|
# Accept int/float/str; auto-detect ms vs s
|
|
@@ -188,7 +196,34 @@ class EzvizCamera:
|
|
|
188
196
|
ts = float(epoch)
|
|
189
197
|
if ts > 1e11: # very likely milliseconds
|
|
190
198
|
ts = ts / 1000.0
|
|
191
|
-
|
|
199
|
+
# Convert epoch to UTC for robust delta; derive display time in camera tz
|
|
200
|
+
event_utc = datetime.datetime.fromtimestamp(ts, tz=datetime.UTC)
|
|
201
|
+
last_alarm_dt = event_utc.astimezone(tzinfo)
|
|
202
|
+
# Some devices appear to report epoch as local time rather than UTC.
|
|
203
|
+
# If the provided string timestamp exists and differs significantly
|
|
204
|
+
# from the epoch-based time, reinterpret the epoch as local time.
|
|
205
|
+
if raw_time_str:
|
|
206
|
+
raw_norm = raw_time_str
|
|
207
|
+
if "Today" in raw_norm:
|
|
208
|
+
raw_norm = raw_norm.replace("Today", str(now_local.date()))
|
|
209
|
+
try:
|
|
210
|
+
dt_str_local = datetime.datetime.strptime(
|
|
211
|
+
raw_norm, "%Y-%m-%d %H:%M:%S"
|
|
212
|
+
).replace(tzinfo=tzinfo)
|
|
213
|
+
diff = abs(
|
|
214
|
+
(
|
|
215
|
+
event_utc
|
|
216
|
+
- dt_str_local.astimezone(datetime.UTC)
|
|
217
|
+
).total_seconds()
|
|
218
|
+
)
|
|
219
|
+
if diff > 120:
|
|
220
|
+
# Reinterpret the epoch as local clock time in camera tz
|
|
221
|
+
naive_utc = datetime.datetime.fromtimestamp(ts, tz=datetime.UTC).replace(tzinfo=None)
|
|
222
|
+
event_local_reint = naive_utc.replace(tzinfo=tzinfo)
|
|
223
|
+
event_utc = event_local_reint.astimezone(datetime.UTC)
|
|
224
|
+
last_alarm_dt = event_local_reint
|
|
225
|
+
except ValueError:
|
|
226
|
+
pass
|
|
192
227
|
except (
|
|
193
228
|
TypeError,
|
|
194
229
|
ValueError,
|
|
@@ -196,21 +231,19 @@ class EzvizCamera:
|
|
|
196
231
|
): # fall back to string parsing below
|
|
197
232
|
last_alarm_dt = None
|
|
198
233
|
|
|
234
|
+
last_alarm_str: str | None = None
|
|
199
235
|
if last_alarm_dt is None:
|
|
200
236
|
# Fall back to string parsing
|
|
201
|
-
raw =
|
|
202
|
-
self._last_alarm.get("alarmStartTimeStr")
|
|
203
|
-
or self._last_alarm.get("alarmTimeStr")
|
|
204
|
-
or ""
|
|
205
|
-
)
|
|
237
|
+
raw = raw_time_str
|
|
206
238
|
if not raw:
|
|
207
239
|
return
|
|
208
240
|
if "Today" in raw:
|
|
209
|
-
raw = raw.replace("Today", str(
|
|
241
|
+
raw = raw.replace("Today", str(now_local.date()))
|
|
210
242
|
try:
|
|
211
243
|
last_alarm_dt = datetime.datetime.strptime(
|
|
212
244
|
raw, "%Y-%m-%d %H:%M:%S"
|
|
213
245
|
).replace(tzinfo=tzinfo)
|
|
246
|
+
last_alarm_str = last_alarm_dt.strftime("%Y-%m-%d %H:%M:%S")
|
|
214
247
|
except ValueError: # Unrecognized format; give up gracefully
|
|
215
248
|
_LOGGER.debug(
|
|
216
249
|
"Unrecognized alarm time format for %s: %s", self._serial, raw
|
|
@@ -218,15 +251,36 @@ class EzvizCamera:
|
|
|
218
251
|
self._alarmmotiontrigger = {
|
|
219
252
|
"alarm_trigger_active": False,
|
|
220
253
|
"timepassed": None,
|
|
254
|
+
"last_alarm_time_str": raw or None,
|
|
221
255
|
}
|
|
222
256
|
return
|
|
257
|
+
else:
|
|
258
|
+
# We selected epoch path; format a human-readable local string
|
|
259
|
+
last_alarm_str = last_alarm_dt.astimezone(tzinfo).strftime(
|
|
260
|
+
"%Y-%m-%d %H:%M:%S"
|
|
261
|
+
)
|
|
223
262
|
|
|
224
|
-
|
|
225
|
-
|
|
263
|
+
# Compute elapsed seconds since the last alarm. If the timestamp is
|
|
264
|
+
# somehow in the future (timezone mismatch or clock skew), do not
|
|
265
|
+
# report a motion trigger; clamp the exposed seconds to 0.0.
|
|
266
|
+
# Use UTC delta when epoch was provided; otherwise compute in camera tz.
|
|
267
|
+
if epoch is not None and last_alarm_dt is not None:
|
|
268
|
+
event_utc_for_delta = last_alarm_dt.astimezone(datetime.UTC)
|
|
269
|
+
delta = now_utc - event_utc_for_delta
|
|
270
|
+
else:
|
|
271
|
+
delta = now_local - last_alarm_dt
|
|
272
|
+
seconds = float(delta.total_seconds())
|
|
273
|
+
if seconds < 0:
|
|
274
|
+
active = False
|
|
275
|
+
seconds_out = 0.0
|
|
276
|
+
else:
|
|
277
|
+
active = seconds < 60.0
|
|
278
|
+
seconds_out = seconds
|
|
226
279
|
|
|
227
280
|
self._alarmmotiontrigger = {
|
|
228
|
-
"alarm_trigger_active":
|
|
229
|
-
"timepassed":
|
|
281
|
+
"alarm_trigger_active": active,
|
|
282
|
+
"timepassed": seconds_out,
|
|
283
|
+
"last_alarm_time_str": last_alarm_str,
|
|
230
284
|
}
|
|
231
285
|
|
|
232
286
|
def _get_tzinfo(self) -> datetime.tzinfo:
|
|
@@ -370,7 +424,10 @@ class EzvizCamera:
|
|
|
370
424
|
"PIR_Status": self.fetch_key(["STATUS", "pirStatus"]),
|
|
371
425
|
"Motion_Trigger": self._alarmmotiontrigger["alarm_trigger_active"],
|
|
372
426
|
"Seconds_Last_Trigger": self._alarmmotiontrigger["timepassed"],
|
|
373
|
-
|
|
427
|
+
# Keep last_alarm_time in sync with the time actually used to
|
|
428
|
+
# compute Motion_Trigger/Seconds_Last_Trigger.
|
|
429
|
+
"last_alarm_time": self._alarmmotiontrigger.get("last_alarm_time_str")
|
|
430
|
+
or self._last_alarm.get("alarmStartTimeStr"),
|
|
374
431
|
"last_alarm_pic": self._last_alarm.get(
|
|
375
432
|
"picUrl",
|
|
376
433
|
"https://eustatics.ezvizlife.com/ovs_mall/web/img/index/EZVIZ_logo.png?ver=3007907502",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|