Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions custom_components/vimar_intercom/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,22 @@
# ─── RTP / Media ──────────────────────────────────────────────────────────────
RTP_AUDIO_PORT = 7200
RTP_VIDEO_PORT = 9200
# Bandwidth we declare in the SDP (b=AS, kbit/s). The 40515 panel's encoder honours it:
# 256 gave ~14 KB keyframes at 16 fps, 2048 gives 30-58 KB at 25 fps (~1.5 Mbit/s, its
# ceiling: 4096 is the same), same 720x576, no loss over the cloud. Session = video + audio.
SDP_VIDEO_BANDWIDTH = 2048
SDP_SESSION_BANDWIDTH = 2200
# Bandwidth we declare in the SDP (b=AS, kbit/s); the panels' encoders honour it.
# Measured over the cloud relay (#161):
# - Tab 5S Up 40515: 2048 gives 720x576 at 24 fps (~1.9 Mbit/s, its ceiling: 4096 is
# the same); 256 drops to 320x240 at 15 fps (~240 kbit/s). No loss on that path at
# either value, first frame ~1.8-1.9 s at both.
# - Tab 7S Up 40517: 2048 gives about ten times the packets of 256 through a relay that
# loses a few percent of them: ten times the damaged frames (smeared, frozen, laggy).
# CONF_VIDEO_BANDWIDTH chooses: "auto" (default) is 2048 on local UDP (no relay in the
# way) and 256 over the cloud, as view_keepalive's default follows the transport;
# "256" and "2048" force one. Session = video + audio.
CONF_VIDEO_BANDWIDTH = "video_bandwidth"
VIDEO_BANDWIDTH_AUTO = "auto"
VIDEO_BANDWIDTH_LOW = "256"
VIDEO_BANDWIDTH_HIGH = "2048"
DEFAULT_VIDEO_BANDWIDTH = VIDEO_BANDWIDTH_AUTO
SDP_BANDWIDTH = {VIDEO_BANDWIDTH_LOW: (256, 512), VIDEO_BANDWIDTH_HIGH: (2048, 2200)}
# Porte locali dell'ffmpeg AV (/api/vimar_intercom/av). Devono essere PARI e
# distanti almeno 2: per ogni riga m= dell'SDP ffmpeg apre la porta RTP **e** la
# RTCP (= RTP + 1). Fino alla 1.0.7 erano 19201/19202: l'RTCP del video cadeva
Expand Down
17 changes: 17 additions & 0 deletions custom_components/vimar_intercom/options_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,13 @@
CONF_HOMEKIT_ANSWER,
CONF_HOMEKIT_RING_BUTTON,
CONF_HOMEKIT_SMOOTH,
CONF_VIDEO_BANDWIDTH,
DEFAULT_HOMEKIT_ACCESSORY,
DEFAULT_HOMEKIT_ANSWER,
DEFAULT_HOMEKIT_RING_BUTTON,
DEFAULT_HOMEKIT_SMOOTH,
DEFAULT_SNAPSHOT_DELAY,
DEFAULT_VIDEO_BANDWIDTH,
DOMAIN,
HOMEKIT_ANSWER_OPEN,
HOMEKIT_ANSWER_TALK,
Expand All @@ -71,6 +73,9 @@
MY_NAME,
PICG_TARGET,
SGA_TARGET,
VIDEO_BANDWIDTH_AUTO,
VIDEO_BANDWIDTH_HIGH,
VIDEO_BANDWIDTH_LOW,
)
from .runtime import (
MEDIA_ENC_MODES,
Expand Down Expand Up @@ -285,6 +290,15 @@ def _photos_schema(form: dict) -> dict:
default=form.get(KEY_VIEW_KA, view_keepalive_default(
form.get(KEY_USE_LOCAL_UDP, True))),
): vol.All(vol.Coerce(int), vol.Range(min=0, max=3600)),
# Every video from the panel: card, camera, /av and HomeKit.
vol.Optional(
CONF_VIDEO_BANDWIDTH,
default=form.get(CONF_VIDEO_BANDWIDTH, DEFAULT_VIDEO_BANDWIDTH),
): selector.SelectSelector(selector.SelectSelectorConfig(
options=[VIDEO_BANDWIDTH_AUTO, VIDEO_BANDWIDTH_LOW, VIDEO_BANDWIDTH_HIGH],
translation_key="video_bandwidth",
mode=selector.SelectSelectorMode.LIST,
)),
}


Expand Down Expand Up @@ -381,6 +395,9 @@ async def async_step_settings(
KEY_SNAP_DIR: snap_dir,
KEY_SNAP_DELAY: snap_delay,
KEY_VIEW_KA: view_ka,
CONF_VIDEO_BANDWIDTH: user_input.get(
CONF_VIDEO_BANDWIDTH,
current.get(CONF_VIDEO_BANDWIDTH, DEFAULT_VIDEO_BANDWIDTH)),
KEY_ALLOWED_USERS: allowed_users,
KEY_RING_WEBHOOK_URL: ring_webhook_url,
KEY_RING_END_WEBHOOK_URL: ring_end_webhook_url,
Expand Down
14 changes: 14 additions & 0 deletions custom_components/vimar_intercom/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,20 @@ def away_message_configured() -> bool:
# Foto di chi suona: cartella (vuoto = non salvare) e secondi dopo lo squillo.
SNAPSHOT_DIR: str = ""
SNAPSHOT_DELAY: int = _const.DEFAULT_SNAPSHOT_DELAY
# kbit/s declared in the SDP (b=AS) for the video and the session: see const.SDP_BANDWIDTH.
VIDEO_BANDWIDTH, SESSION_BANDWIDTH = _const.SDP_BANDWIDTH[_const.VIDEO_BANDWIDTH_LOW]
VIEW_KEEPALIVE: float = _const.DEFAULT_VIEW_KEEPALIVE_CLOUD # s di silenzio a "Vedi esterno", 0 = nessuno


def video_bandwidth_choice(option, use_local_udp: bool) -> str:
"""The bandwidth to ask for (const.SDP_BANDWIDTH key): the option when it is
one of the values, else (automatic, unset, unknown) by transport: 2048 on
local UDP, 256 over the cloud relay, which loses packets (#161)."""
if str(option) in _const.SDP_BANDWIDTH:
return str(option)
return _const.VIDEO_BANDWIDTH_HIGH if use_local_udp else _const.VIDEO_BANDWIDTH_LOW


def view_keepalive_default(use_local_udp: bool) -> int:
"""Locale (UDP, il 2 fili): niente silenzio, occuperebbe l'appartamento; cloud: 120 s."""
return 0 if use_local_udp else _const.DEFAULT_VIEW_KEEPALIVE_CLOUD
Expand Down Expand Up @@ -259,6 +270,7 @@ def configure(data: dict) -> None:
global CAMERA_TARGET, INTERNAL_PANEL_TARGET, DOOR_TARGET, CAMERA_TARGET_CONFIGURED
global AWAY_MESSAGE_FILE, AWAY_MESSAGE_TEXT, AWAY_MESSAGE_TTS, AWAY_MESSAGE_DELAY
global SNAPSHOT_DIR, SNAPSHOT_DELAY, VIEW_KEEPALIVE, ALLOWED_USERS
global VIDEO_BANDWIDTH, SESSION_BANDWIDTH
global RING_WEBHOOK_URL, RING_END_WEBHOOK_URL
global DEVICE_IMEI, DEVICE_UUID, DEVICE_NAME, AV_KEY

Expand Down Expand Up @@ -355,6 +367,8 @@ def configure(data: dict) -> None:
SNAPSHOT_DELAY = int(data.get("snapshot_delay", _const.DEFAULT_SNAPSHOT_DELAY))
_ka = data.get("view_keepalive")
VIEW_KEEPALIVE = int(_ka) if _ka is not None else view_keepalive_default(USE_LOCAL_UDP)
VIDEO_BANDWIDTH, SESSION_BANDWIDTH = _const.SDP_BANDWIDTH[
video_bandwidth_choice(data.get(_const.CONF_VIDEO_BANDWIDTH), USE_LOCAL_UDP)]
ALLOWED_USERS = [str(u) for u in data.get("allowed_users") or []]
RING_WEBHOOK_URL = str(data.get("ring_webhook_url") or "").strip()
RING_END_WEBHOOK_URL = str(data.get("ring_end_webhook_url") or "").strip()
Expand Down
4 changes: 2 additions & 2 deletions custom_components/vimar_intercom/sdp.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ def _key(sec, current):
),
"video": (
f"m=video {video_port} {video_proto} {' '.join(pt for pt, _ in h264)}\r\n"
f"b=AS:{C.SDP_VIDEO_BANDWIDTH}\r\n"
f"b=AS:{R.VIDEO_BANDWIDTH}\r\n"
f"{h264_lines}"
f"a=sendrecv\r\n"
f"{video_crypto}"
Expand All @@ -191,7 +191,7 @@ def _key(sec, current):
f"o=- {sid} {sid} IN IP4 {MY_IP}\r\n"
f"s=Talk\r\n"
f"c=IN IP4 {MY_IP}\r\n"
f"b=AS:{C.SDP_SESSION_BANDWIDTH}\r\n"
f"b=AS:{R.SESSION_BANDWIDTH}\r\n"
f"t=0 0\r\n"
f"a=rtcp-xr:rcvr-rtt=all:10000 stat-summary=loss,dup,jitt,TTL voip-metrics\r\n"
+ "".join(blocks.get(kind) or _refused_block(offer[kind])
Expand Down
13 changes: 11 additions & 2 deletions custom_components/vimar_intercom/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,8 @@
"view_keepalive": "Secondi di silenzio a «Vedi esterno» (0 = nessuno)",
"allowed_users": "Utenti ammessi a squilli e media live (vuoto = tutti)",
"ring_webhook_url": "Webhook a inizio squillo (URL)",
"ring_end_webhook_url": "Webhook a fine squillo (URL)"
"ring_end_webhook_url": "Webhook a fine squillo (URL)",
"video_bandwidth": "Qualità video dalla targa"
},
"data_description": {
"voice_answer": "Chi può rispondere alla chiamata parlando mentre squilla. Solo dichiarato: risponde solo chi si collega con /audio_ws?voice_answer=1 (Echo Show, HomeKit via Scrypted). Mai: la voce non risponde. Chiunque: risponde qualsiasi connessione con microfono; attenzione, un tablet a muro con il microfono rimasto aperto può rispondere da solo con i rumori di casa.",
Expand All @@ -133,7 +134,8 @@
"view_keepalive": "Durante la vista dell'esterno l'integrazione manda silenzio audio, senza il quale via cloud la targa chiude la vista dopo ~10 s. Si ferma dopo questi secondi dall'inizio della vista (poi la vista si chiude da sola). Sul 2 fili in locale il silenzio tiene occupato l'appartamento: il predefinito è 0 in locale e 120 via cloud; 0 = nessun silenzio (sul 2 fili va bene 0 o 30).",
"allowed_users": "Limita la card, lo storico squilli (foto e clip) e /audio_ws a questi utenti di Home Assistant. Gli amministratori sempre. Vuoto = ogni utente autenticato. NON limita l'entità camera né /av dalla rete locale: chi può aprire la camera vede (e sente) lo stream; e se la cartella foto è sotto una cartella media di HA, foto e clip compaiono nel browser media per tutti gli utenti.",
"ring_webhook_url": "GET (fire-and-forget) inviata quando inizia uno squillo, es. per accendere un interruttore fittizio (Scrypted Dummy Switch) collegato a un Custom Doorbell Button. Un fallimento si logga come warning e non ferma lo squillo. Vuoto = disattivato.",
"ring_end_webhook_url": "GET (fire-and-forget) inviata quando lo squillo finisce, risposto, annullato o scaduto: stessa idea di sopra, per spegnere l'interruttore. Vuoto = disattivato."
"ring_end_webhook_url": "GET (fire-and-forget) inviata quando lo squillo finisce, risposto, annullato o scaduto: stessa idea di sopra, per spegnere l'interruttore. Vuoto = disattivato.",
"video_bandwidth": "Vale per tutto il video della targa: card, camera, /av e HomeKit. Automatico (consigliato): 2 Mbit/s in UDP locale, 256 kbit/s tramite il relay cloud, che perde qualche pacchetto su cento. 256 kbit/s: flusso più leggero, meno fotogrammi rovinati con un relay che perde pacchetti, ma immagine più piccola su alcune targhe (un Tab 5S Up 40515 scende a 320x240 a 15 fps). 2 Mbit/s: l'immagine più nitida (720x576 a 24 fps su un 40515) e dieci volte il traffico; con un relay che perde pacchetti (misurato su un Tab 7S Up 40517) il video si sgrana, si blocca e va in ritardo."
}
},
"homekit": {
Expand Down Expand Up @@ -442,6 +444,13 @@
"talk": "Rispondi quando parli",
"open": "Rispondi all'apertura"
}
},
"video_bandwidth": {
"options": {
"auto": "Automatico (2 Mbit/s in locale, 256 kbit/s in cloud)",
"256": "256 kbit/s (risoluzione più bassa su alcune targhe)",
"2048": "2 Mbit/s (più nitido, serve una connessione pulita)"
}
}
},
"exceptions": {
Expand Down
13 changes: 11 additions & 2 deletions custom_components/vimar_intercom/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,8 @@
"view_keepalive": "Seconds of silence for “Vedi esterno” (0 = none)",
"allowed_users": "Users allowed to see rings and live media (empty = everyone)",
"ring_webhook_url": "Ring-start webhook (URL)",
"ring_end_webhook_url": "Ring-end webhook (URL)"
"ring_end_webhook_url": "Ring-end webhook (URL)",
"video_bandwidth": "Video quality from the panel"
},
"data_description": {
"voice_answer": "Who can answer the call by talking while it rings. Declared only: only connections using /audio_ws?voice_answer=1 (Echo Show, HomeKit via Scrypted). Never: talking never answers. Anyone: any connection with a microphone can answer; beware, a wall tablet whose microphone was left open can answer by itself with household noise.",
Expand All @@ -133,7 +134,8 @@
"view_keepalive": "While viewing the outside the integration sends audio silence; without it, over the cloud the panel closes the view after ~10 s. It stops this many seconds after the view starts (the view then ends by itself). On the 2-wire plant in local mode the silence keeps the apartment busy: the default is 0 in local mode and 120 over the cloud; 0 = no silence (on the 2-wire use 0 or 30).",
"allowed_users": "Limits the card, the ring history (photos and clips) and /audio_ws to these Home Assistant users. Admins always. Empty = every logged-in user. It does NOT limit the camera entity or /av from the local network: anyone who can open the camera sees (and hears) the stream; and if the photo folder is under an HA media directory, photos and clips show up in the media browser for every user.",
"ring_webhook_url": "GET (fire-and-forget) sent when a ring starts, e.g. to turn on a dummy switch (Scrypted Dummy Switch) linked to a Custom Doorbell Button. A failure is only logged as a warning and never stops the ring. Empty = off.",
"ring_end_webhook_url": "GET (fire-and-forget) sent when the ring ends, answered, cancelled or timed out: same idea as above, to turn the switch off. Empty = off."
"ring_end_webhook_url": "GET (fire-and-forget) sent when the ring ends, answered, cancelled or timed out: same idea as above, to turn the switch off. Empty = off.",
"video_bandwidth": "Applies to every video from the panel: the card, the camera, /av and HomeKit. Automatic (recommended): 2 Mbit/s on local UDP, 256 kbit/s over the cloud relay, which loses a few packets in a hundred. 256 kbit/s: a lighter stream, fewer damaged frames on a lossy relay, but a smaller picture on some panels (a Tab 5S Up 40515 drops to 320x240 at 15 fps). 2 Mbit/s: the sharpest picture (720x576 at 24 fps on a 40515) and ten times the traffic; on a relay that loses packets (measured on a Tab 7S Up 40517) the video smears, freezes and lags."
}
},
"homekit": {
Expand Down Expand Up @@ -442,6 +444,13 @@
"talk": "Answer when you talk",
"open": "Answer when the view opens"
}
},
"video_bandwidth": {
"options": {
"auto": "Automatic (2 Mbit/s locally, 256 kbit/s over the cloud)",
"256": "256 kbit/s (lower resolution on some panels)",
"2048": "2 Mbit/s (sharper, needs a clean connection)"
}
}
},
"exceptions": {
Expand Down
13 changes: 11 additions & 2 deletions custom_components/vimar_intercom/translations/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,8 @@
"view_keepalive": "Secondi di silenzio a «Vedi esterno» (0 = nessuno)",
"allowed_users": "Utenti ammessi a squilli e media live (vuoto = tutti)",
"ring_webhook_url": "Webhook a inizio squillo (URL)",
"ring_end_webhook_url": "Webhook a fine squillo (URL)"
"ring_end_webhook_url": "Webhook a fine squillo (URL)",
"video_bandwidth": "Qualità video dalla targa"
},
"data_description": {
"voice_answer": "Chi può rispondere alla chiamata parlando mentre squilla. Solo dichiarato: risponde solo chi si collega con /audio_ws?voice_answer=1 (Echo Show, HomeKit via Scrypted). Mai: la voce non risponde. Chiunque: risponde qualsiasi connessione con microfono; attenzione, un tablet a muro con il microfono rimasto aperto può rispondere da solo con i rumori di casa.",
Expand All @@ -133,7 +134,8 @@
"view_keepalive": "Durante la vista dell'esterno l'integrazione manda silenzio audio, senza il quale via cloud la targa chiude la vista dopo ~10 s. Si ferma dopo questi secondi dall'inizio della vista (poi la vista si chiude da sola). Sul 2 fili in locale il silenzio tiene occupato l'appartamento: il predefinito è 0 in locale e 120 via cloud; 0 = nessun silenzio (sul 2 fili va bene 0 o 30).",
"allowed_users": "Limita la card, lo storico squilli (foto e clip) e /audio_ws a questi utenti di Home Assistant. Gli amministratori sempre. Vuoto = ogni utente autenticato. NON limita l'entità camera né /av dalla rete locale: chi può aprire la camera vede (e sente) lo stream; e se la cartella foto è sotto una cartella media di HA, foto e clip compaiono nel browser media per tutti gli utenti.",
"ring_webhook_url": "GET (fire-and-forget) inviata quando inizia uno squillo, es. per accendere un interruttore fittizio (Scrypted Dummy Switch) collegato a un Custom Doorbell Button. Un fallimento si logga come warning e non ferma lo squillo. Vuoto = disattivato.",
"ring_end_webhook_url": "GET (fire-and-forget) inviata quando lo squillo finisce, risposto, annullato o scaduto: stessa idea di sopra, per spegnere l'interruttore. Vuoto = disattivato."
"ring_end_webhook_url": "GET (fire-and-forget) inviata quando lo squillo finisce, risposto, annullato o scaduto: stessa idea di sopra, per spegnere l'interruttore. Vuoto = disattivato.",
"video_bandwidth": "Vale per tutto il video della targa: card, camera, /av e HomeKit. Automatico (consigliato): 2 Mbit/s in UDP locale, 256 kbit/s tramite il relay cloud, che perde qualche pacchetto su cento. 256 kbit/s: flusso più leggero, meno fotogrammi rovinati con un relay che perde pacchetti, ma immagine più piccola su alcune targhe (un Tab 5S Up 40515 scende a 320x240 a 15 fps). 2 Mbit/s: l'immagine più nitida (720x576 a 24 fps su un 40515) e dieci volte il traffico; con un relay che perde pacchetti (misurato su un Tab 7S Up 40517) il video si sgrana, si blocca e va in ritardo."
}
},
"homekit": {
Expand Down Expand Up @@ -442,6 +444,13 @@
"talk": "Rispondi quando parli",
"open": "Rispondi all'apertura"
}
},
"video_bandwidth": {
"options": {
"auto": "Automatico (2 Mbit/s in locale, 256 kbit/s in cloud)",
"256": "256 kbit/s (risoluzione più bassa su alcune targhe)",
"2048": "2 Mbit/s (più nitido, serve una connessione pulita)"
}
}
},
"exceptions": {
Expand Down
1 change: 1 addition & 0 deletions docs/CONFIGURATION.it.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ Impostazioni → Vimar Intercom → **Configura**:
| **Cartella foto squillo** (`snapshot_dir`) | dove salvare, a ogni squillo, la foto di chi suona (`squillo_AAAAMMGG_HHMMSS_mmm.jpg` + `ultimo_squillo.jpg`) e il clip dello squillo (`squillo_AAAAMMGG_HHMMSS_mmm.mp4`: il video dell'anteprima, e della chiamata se si risponde da HA, fino a 60 s, senza audio), es. `/config/media/citofono`. Deve essere scrivibile da HA. Vuoto = disattivato |
| **Secondi per la foto migliore** (`snapshot_delay`) | la prima foto si salva appena arriva il primo fotogramma (~1 s dallo squillo); dopo questi secondi la sostituisce un fotogramma con l'esposizione regolata (il primo keyframe della targa è scuro). Default 3 (Tab 5S Up 40515), 0 = resta la prima |
| **Silenzio della vista** (`view_keepalive`) | secondi di silenzio audio mandati durante «Vedi esterno» (0 = nessuno). Via cloud senza silenzio la targa chiude la vista a ~10 s; sul 2 fili in locale tiene occupato l'appartamento (fino a 300 s). Default: 120 via cloud, 0 in locale; sul 2 fili metti 0 o 30 |
| **Qualità video dalla targa** (`video_bandwidth`) | la banda video chiesta alla targa, per tutto il video (card, camera, `/av`, HomeKit). **Automatico** (default): 2 Mbit/s in UDP locale, 256 kbit/s tramite il relay cloud. **256 kbit/s**: meno fotogrammi rovinati con un relay che perde pacchetti, ma immagine più piccola su alcune targhe (un Tab 5S Up 40515 scende a 320x240 a 15 fps). **2 Mbit/s**: l'immagine più nitida (720x576 a 24 fps su un 40515), dieci volte il traffico; con un relay che perde pacchetti (un Tab 7S Up 40517) il video si sgrana, si blocca e va in ritardo |
| **Utenti ammessi** (`allowed_users`) | Limita la card, lo storico squilli (`GET /api/vimar_intercom/rings`, foto e clip) e `/audio_ws` a questi utenti HA. Gli amministratori sempre. Vuoto = ogni utente autenticato (default). **Non** è per utente per l'entità camera né per `/av` usato con la sua chiave (la camera di HA, go2rtc: solo rete locale, vedi sotto): chi può aprire la camera vede e sente lo stream. Se la cartella foto è sotto una cartella media di HA (es. `/config/media/citofono`), foto e clip compaiono nel browser media per tutti gli utenti |
| **Messaggio di assenza** (`away_message_file`, `away_message_delay`) | file audio (mp3, wav…) fatto sentire al visitatore se nessuno risponde entro N secondi; poi l'integrazione riaggancia. Se il Tab espone il ritardo della segreteria si usa quello (*Segreteria · ritardo*) |
| **Messaggio di assenza da testo** (`away_message_text`, `away_message_tts`) | se il campo file è vuoto, questo testo lo legge la sintesi vocale di Home Assistant (`away_message_tts` = un'entità `tts.*`; vuoto = il motore predefinito di HA) nella lingua di HA, max 30 s. L'audio si genera all'avvio e resta in cache; se il TTS fallisce il citofono squilla come sempre |
Expand Down
Loading
Loading