feat(packaging): nest protocol/ + ocf/ under smartthings_local, add PyPI packaging
Nest the two library packages under a single import namespace so they can ship as one distribution: protocol/ -> smartthings_local/protocol/ ocf/ -> smartthings_local/ocf/ (git mv, history preserved) - Rewrite all imports protocol.* -> smartthings_local.protocol.*, ocf.* -> smartthings_local.ocf.* across the ocf modules, mqtt_demo/ (bridge, descriptor, samples), and tests. - Add pyproject.toml: dist name `smartthings-local`, hatch-vcs versioning from v* tags, wheel ships only smartthings_local/. - Add .github/workflows/publish.yml: build + PyPI Trusted Publishing on v* tags (OIDC, no stored token). - Force-include protocol/ocf_root_ca.pem via [tool.hatch.build] artifacts: it is tracked but matches .gitignore's *.pem, so hatchling's VCS file selection would drop it — and dtls_session.py loads it at runtime. - Update mqtt_demo Dockerfile COPY and deploy.sh tar allowlist to the single smartthings_local/ package. - .gitignore: build artifacts (_version.py, dist/, *.egg-info/). Validated: pytest tests/ (11 passed), python -m build produces sdist + wheel with the pem bundled, fresh pip install resolves all nested imports with the pem readable from site-packages.
This commit is contained in:
1 parent
9004bac729
commit
3fdc735141
23 files changed
+155
-37
No files matched your search
@@ -0,0 +1,50 @@
|
||||
"""Periodic OBSERVE re-subscribe.
|
||||
|
||||
CoAP OBSERVE (RFC 7641) has no built-in TTL, but real-world peers age
|
||||
out observer state on their own schedule — Samsung's RT-OCF is known
|
||||
to silently drop notify delivery during cloud auth blips even though
|
||||
the DTLS session stays healthy. Without a re-subscribe, recovery from
|
||||
such a blip requires a full session reconnect.
|
||||
|
||||
This task derregisters the current observer tokens and re-subscribes
|
||||
every `interval_s`. Cheap (one register CON per path), idempotent
|
||||
(Samsung silently no-ops a register on an already-active token), and
|
||||
resilient — individual subscribe failures are logged but don't abort
|
||||
the task.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import threading
|
||||
from typing import Optional
|
||||
|
||||
from smartthings_local.protocol.dtls_session import DtlsCoapSession
|
||||
|
||||
|
||||
class ObserveRefreshTask:
|
||||
|
||||
def __init__(self,
|
||||
session: DtlsCoapSession,
|
||||
paths,
|
||||
interval_s: float = 6 * 3600.0,
|
||||
logger=None):
|
||||
self.session = session
|
||||
self.paths = [list(p) for p in paths]
|
||||
self.interval_s = interval_s
|
||||
self.log = logger
|
||||
self._refresh_count = 0
|
||||
|
||||
@property
|
||||
def refresh_count(self) -> int:
|
||||
return self._refresh_count
|
||||
|
||||
def run_forever(self, stop: threading.Event) -> None:
|
||||
while not stop.wait(self.interval_s):
|
||||
try:
|
||||
self.session.refresh_observes(self.paths)
|
||||
self._refresh_count += 1
|
||||
if self.log:
|
||||
self.log.info("OBSERVE refresh #%d (%d paths)",
|
||||
self._refresh_count, len(self.paths))
|
||||
except Exception as e:
|
||||
if self.log:
|
||||
self.log.warning("OBSERVE refresh: %s", e)
|
||||
Reference in new issue
Block a user