post removed by author
post removed by author
so I got it to discover by using auto:deviceid, but it still says no channels found. logs below:
toml:
# zapperbridge.toml -- ONE file for every choice the user has to make.
# Copy to ./zapperbridge.toml, edit, and mount it at /config/zapperbridge.toml
# (docker-compose.yml already does). Validate without starting anything:
# docker compose run --rm zapperbridge check
# Every key is optional; the defaults are the safe ones.
# ---------------------------------------------------------------------------
# 1. WHICH M1s? One [[m1]] block per ZapperBox M1 (or leave all out).
# On each M1: turn ON "sharing" in the Zapper app first, or it answers
# NOT_SHARED. Each M1 becomes its own virtual HDHomeRun with its own
# HDHR DeviceID, tuners, and block of device ids + push IPs.
# ---------------------------------------------------------------------------
# No [[m1]] block at all => one M1, auto-discovered from its UDP 31064
# announce (works with exactly one M1 on the LAN).
#
[[m1]]
name = "living-room"
address = "auto:acdbda698f08" # an IP, "auto", or "auto:<m1 device id>"
leases = 3 # BRP tuners to register, 1-3 (the M1 grants 3)
# hls_tuner = true # also offer the M1's fixed HLS tuner (+1)
# # advanced, usually leave out (derived, stable across restarts):
device_id = "02BB0012" # HDHR DeviceID, 8 hex
# # devs = ["02bb00aaaaaa", "02bb00bbbbbb", "02bb00cccccc"]
push_ips = ["192.0.2.240", "192.0.2.241", "192.0.2.242"]
# #advertise_ip = "192.168.1.29" # this virtual HDHR's own IP (see [network])
# # friendly_name = "Living room M1"
#
# [[m1]] # a second M1: just add another block
# name = "garage"
# address = "auto:02bb00a00001" # pick this M1 out by its device id
# advertise_ip = "192.0.2.61" # give each extra M1 its own IP (macvlan/ipvlan)
# # Without its own advertise_ip, extra M1s get shifted HTTP/TS ports and
# # HDHR clients may only find the one on :80.
# ---------------------------------------------------------------------------
# 2. PUSH IPs. The M1 sends each tuner's video to the IP we announce for
# that tuner's device id, TCP :31061. Every tuner needs ITS OWN IP, and the
# container (or host) must really own it so it can answer ARP.
# Never use an M1's own address (always rejected), a real device's address
# (list those in reserved_ips below), or anything else already in use.
# ---------------------------------------------------------------------------
[push]
# A list, ranges, or CIDR. Need one IP per tuner (3 per M1) -- a block you
# have reserved outside your router's DHCP range.
pool = [] # e.g. ["192.0.2.240-249"]
# # e.g. ["192.0.2.240", "192.0.2.241-243"]
# device ids are derived from prefix + sha1(seed|m1|slot). Leave device_seed
# UNSET (recommended): on first run a random per-install seed is generated and
# saved in the data dir (/data/seed, or $ZB_DATA_DIR/seed; mount /data as a
# volume), and reused on every restart, so ids are stable and differ between
# installs. The M1 remembers registered ids, so keep that file: deleting it
# changes every device id. Setting device_seed (or ZB_DEVICE_SEED) overrides
# the file; `check` shows where the seed came from.
# device_seed = "choose-your-own"
device_prefix = "02bb00" # 2-8 hex digits
# reserved_ips: addresses of REAL devices on your LAN that must never be used
# as a push IP (validation rejects them). Nothing is built in -- this list is
# yours. Same forms as pool (single IPs, ranges, CIDR). An M1's own address is
# always rejected on top of this, no need to list it.
# Example: a real Zapper Mini (say at 192.0.2.200) would steal the M1's TS
# pushes if we pointed a virtual tuner at it, so list it:
# reserved_ips = ["192.0.2.200"]
#
# check_free: before starting, send ONE ping and read the ARP/neighbour table
# for each planned push IP; if something already answers, fail (or just warn,
# see below) so two machines never fight over an address. Nothing is sent to
# an M1 or to reserved IPs, nothing is spoofed, and addresses this host
# already owns (e.g. aliases you added) are fine. On by default. Turn off with
# check_free = false, env ZB_CHECK_FREE=0, or `entrypoint.py check --skip-free-check`.
# check_free = true
# check_free_fail = true # false = only warn when an IP answers
# never_devs = [] # extra device ids to refuse
# ---------------------------------------------------------------------------
# 3. NETWORK MODE (must match the compose network you pick)
# host Linux only. Container shares the host NIC. Add each push IP as
# an alias on the host (printed by `check`, never run for you).
# macvlan Linux, wired Ethernet. Container gets its own LAN IPs. Wi-Fi
# and most hypervisor NICs drop macvlan -> use ipvlan.
# ipvlan Linux, L2 mode. Works on Wi-Fi; all IPs share the host MAC.
# Docker Desktop on macOS/Windows cannot do any of these: its VM hides the
# LAN. On a Mac keep running the bridge natively.
# ---------------------------------------------------------------------------
[network]
mode = "host"
# advertise_ip = "192.0.2.60" # REQUIRED for macvlan/ipvlan: the IP clients
# # use to reach the HDHR JSON/TS ports
# http_port = 80 # HDHR JSON (clients expect 80)
# ts_port = 5004
# ssdp = true # UPnP/SSDP announcements (so apps auto-find the HDHR).
# # false (or ZB_SSDP=0) = stay silent; add it in apps by IP.
# # Use false for test runs on a node/host that is not
# # meant to show up as a second HDHomeRun on the LAN.
# ---------------------------------------------------------------------------
# 4. WHAT CLIENTS SEE (HDHomeRun fidelity)
# ---------------------------------------------------------------------------
[hdhr]
mode = "per-m1" # "per-m1": one HDHR per M1 (works today)
# "merged": one HDHR, merged lineup, total TunerCount. PLAN-ONLY:
# `check` validates it, `run` refuses (docs/MERGED-LINEUP.md)
# device_id = "02BB0099" # merged mode: the one HDHR DeviceID (8 hex; derived if unset)
# tuner_cap = 4 # cap the TOTAL advertised TunerCount
hls_tuner = true # default for [[m1]].hls_tuner
# friendly_name = "ZapperBox M1 (emulated)"
# --- opt-in, off by default, RESERVED (accepted, ignored until shipped) ---
# discovery_responder = false # answer HDHR UDP 65001 discovery
# checksum_device_id = false # real-HDHR-style checksum DeviceID
# # (clients may rescan/duplicate: opt in only
# # on a fresh install)
# ---------------------------------------------------------------------------
# 4b. DISCOVERY (dry-run planner; nothing here sends anything)
# `docker compose run --rm zapperbridge discover --listen 60` listens
# passively on UDP 31064 (receive-only), reads each M1's tuner count from
# its announce, and prints the device ids + push IPs it WOULD register.
# A real Mini seen on the LAN is added to reserved_ips automatically.
# Sending an ANNOUNCE needs the explicit command-line flag `--apply`;
# no key in this file can turn it on.
# ---------------------------------------------------------------------------
[discovery]
# listen_seconds = 0 # default for `discover` (0 = configured M1s only)
# sweep = [] # e.g. ["192.0.2.0/24"]: TCP-connect check of
# # the BRP port only, no bytes sent; off by default
# ---------------------------------------------------------------------------
# 5. WEB UI (read-only status pages at http://<host>/ui, see docs/WEBUI.md)
# "off" (default) /ui does not exist.
# "readonly" GET-only dashboard on the same :80 server. Private-LAN source
# addresses only. Set env ZUI_TOKEN (>= 8 chars) to also require
# a token (Bearer header, or open /ui?token=... once to get a
# cookie). Never port-forward it. It listens wherever :80 does
# (all interfaces on host networking).
# "admin" RESERVED: accepted but nothing implements it.
# ---------------------------------------------------------------------------
[ui]
mode = "off" # "off", "readonly" or "admin"
# admin needs a token of >=16 chars in env ZUI_ADMIN_TOKEN (not in this file)
# ---------------------------------------------------------------------------
# 6. REQUEST LOG (JSON lines of stream open/close; path /data/zapperbridge-req.jsonl,
# override with env ZB_REQLOG_PATH; see docs/WEBUI.md)
# ---------------------------------------------------------------------------
[reqlog]
enabled = false
retention_days = 7
max_mb = 50
running natively, no docker I get this:
user@channels:~/Documents/zapperbridge$ sudo python3 hdhr_emulator.py
full-stream diagnostics: ts_analysis=on gap_ms=100 capture_seconds=0
m1 auto-discovery (auto): waiting for the M1 in the background; serving now. auto-discovery listens for the M1's UDP 31064 broadcast, which does not reach a Docker bridge network or another subnet/VLAN: use host or macvlan networking, or skip discovery by setting the M1 address (ZM1=<ip> for the bare facade, ZB_M1=<ip> or [[m1]] address = "<ip>" in the config)
HDHR emulator: JSON on :80 TS-redirect on :5004
m1 auto-discovery (auto): 192.168.1.54 dev=acdbda698f08 (only M1 seen)
ssdp on :1900 group 239.255.255.250 if 192.168.1.29 location http://192.168.1.29/device.xml udn uuid:857c0c04-0f4c-5f2b-a027-4c9cd5045e64
epg refresh fail: RuntimeError('lineup unavailable: api8 failed and no cache') (serving last-good=False)
lineup: M1 unreachable or lineup fetch failed; no cache, serving []
epg watch err: RuntimeError('lineup unavailable: api8 failed and no cache')
lineup: M1 unreachable or lineup fetch failed; no cache, serving []
^Csignal 2: shutting down
ssdp byebye sent
Ok let me take a look
fyi arping shows 100% packet loss, and my nic is not eth0 it is eno12409np0. I also tested it on a different machine natively same error
edit: actually I have 2 nics.
thanks
Looks good. Too bad I dont have a Zapperbox to test (too expensive lol).
I hope the price goes down on those units when the 2nd digital transition happens
Thanks for the log, that helps a lot. The main issue is that the push IPs are still the placeholders from the compose file (192.0.2.240–242). Those are documentation-only addresses and aren't on your network, so the ZapperBox can't send to them. That's why the lineup comes back empty ("api8 failed"), and Channels won't pick up a tuner with no channels.
- Pick 3 unused IPs on the same subnet as your ZapperBox (192.168.1.x), outside your router's DHCP range.
- Remove the 192.0.2.x aliases, then add the new ones on your real NIC, for example:
sudo ip addr add 192.168.1.X/32 dev eno12409np0. Runarping -D -I eno12409np0 -c 2 192.168.1.Xfirst. 100% loss there is what you want, because it means the address is free. - Put those 3 IPs in ZPUSH_IPS.
- Since you have 2 NICs, set
ZB_M1=192.168.1.54and set the advertise IP to the address of the NIC that's on the ZapperBox's network (looks like 192.168.1.29), so nothing gets announced on the wrong interface. - Restart the container and check that the log no longer says "lineup: ... serving []". Then, if Channels still doesn't find it on its own, add it manually by IP: http://192.168.1.29.
If the lineup still fails after that, please post the new log.
should I use /32 or /24 as my lan is /24
no dice. logs below:
WARN ui.mode = "readonly" without ZUI_TOKEN: the UI is open to every private-LAN address that can reach :80 (set env ZUI_TOKEN to require a token; never expose it to the internet)
WARN network.advertise_ip unset: the facade will pick the route-selected primary LAN IP
device id seed: persisted seed file /data/seed
network mode: host | advertised TunerCount total: 4
M1 living-room addr=auto:acdbda698f08 hdhr=02BB0012 tuners=4 http=80 ts=5004
dev 02bb0033c56a -> push 192.168.1.81:31061
dev 02bb00ea1c5e -> push 192.168.1.82:31061
dev 02bb00bb2603 -> push 192.168.1.83:31061
+ 1 fixed HLS tuner (not retunable)
host mode: add each push IP as an alias on the host NIC (not done for you):
sudo ip addr add 192.168.1.81/32 dev eth0 # arping -D -I eth0 -c 2 192.168.1.81 first
sudo ip addr add 192.168.1.82/32 dev eth0 # arping -D -I eth0 -c 2 192.168.1.82 first
sudo ip addr add 192.168.1.83/32 dev eth0 # arping -D -I eth0 -c 2 192.168.1.83 first
info: push ip 192.168.1.81: already assigned to this host
info: push ip 192.168.1.82: already assigned to this host
info: push ip 192.168.1.83: already assigned to this host
starting facade for living-room
push-ip: seed 02bb0033c56a -> 192.168.1.81 (ZPUSH_IPS)
push-ip: seed 02bb00bb2603 -> 192.168.1.83 (ZPUSH_IPS)
push-ip: seed 02bb00ea1c5e -> 192.168.1.82 (ZPUSH_IPS)
full-stream diagnostics: ts_analysis=on gap_ms=100 capture_seconds=0
m1 auto-discovery (auto:acdbda698f08): waiting for the M1 in the background; serving now. auto-discovery listens for the M1's UDP 31064 broadcast, which does not reach a Docker bridge network or another subnet/VLAN: use host or macvlan networking, or skip discovery by setting the M1 address (ZM1=<ip> for the bare facade, ZB_M1=<ip> or [[m1]] address = "<ip>" in the config)
HDHR emulator: JSON on :80 TS-redirect on :5004
m1 auto-discovery (auto:acdbda698f08): 192.168.1.54 dev=acdbda698f08 (matched acdbda698f08)
ssdp on :1900 group 239.255.255.250 if 192.168.1.29 location http://192.168.1.29/device.xml udn uuid:d5842096-00cb-5f2c-a3ae-35e8b7375c81
epg refresh fail: RuntimeError('lineup unavailable: api8 failed and no cache') (serving last-good=False)
lineup: M1 unreachable or lineup fetch failed; no cache, serving []
my new toml:
# zapperbridge.toml -- ONE file for every choice the user has to make.
# Copy to ./zapperbridge.toml, edit, and mount it at /config/zapperbridge.toml
# (docker-compose.yml already does). Validate without starting anything:
# docker compose run --rm zapperbridge check
# Every key is optional; the defaults are the safe ones.
# ---------------------------------------------------------------------------
# 1. WHICH M1s? One [[m1]] block per ZapperBox M1 (or leave all out).
# On each M1: turn ON "sharing" in the Zapper app first, or it answers
# NOT_SHARED. Each M1 becomes its own virtual HDHomeRun with its own
# HDHR DeviceID, tuners, and block of device ids + push IPs.
# ---------------------------------------------------------------------------
# No [[m1]] block at all => one M1, auto-discovered from its UDP 31064
# announce (works with exactly one M1 on the LAN).
#
[[m1]]
name = "living-room"
address = "auto:acdbda698f08" # an IP, "auto", or "auto:<m1 device id>"
leases = 3 # BRP tuners to register, 1-3 (the M1 grants 3)
# # hls_tuner = true # also offer the M1's fixed HLS tuner (+1)
# # advanced, usually leave out (derived, stable across restarts):
device_id = "02BB0012" # HDHR DeviceID, 8 hex
# # devs = ["02bb00aaaaaa", "02bb00bbbbbb", "02bb00cccccc"]
push_ips = ["192.168.1.81", "192.168.1.82", "192.168.1.83"]
advertise_ip = "192.168.1.29" # this virtual HDHR's own IP (see [network])
# # friendly_name = "Living room M1"
#
# [[m1]] # a second M1: just add another block
# name = "garage"
# address = "auto:02bb00a00001" # pick this M1 out by its device id
# advertise_ip = "192.0.2.61" # give each extra M1 its own IP (macvlan/ipvlan)
# # Without its own advertise_ip, extra M1s get shifted HTTP/TS ports and
# # HDHR clients may only find the one on :80.
# ---------------------------------------------------------------------------
# 2. PUSH IPs. The M1 sends each tuner's video to the IP we announce for
# that tuner's device id, TCP :31061. Every tuner needs ITS OWN IP, and the
# container (or host) must really own it so it can answer ARP.
# Never use an M1's own address (always rejected), a real device's address
# (list those in reserved_ips below), or anything else already in use.
# ---------------------------------------------------------------------------
[push]
# A list, ranges, or CIDR. Need one IP per tuner (3 per M1) -- a block you
# have reserved outside your router's DHCP range.
# pool = ["192.0.2.240-249"] # e.g. ["192.0.2.240-249"]
# # e.g. ["192.0.2.240", "192.0.2.241-243"]
# device ids are derived from prefix + sha1(seed|m1|slot). Leave device_seed
# UNSET (recommended): on first run a random per-install seed is generated and
# saved in the data dir (/data/seed, or $ZB_DATA_DIR/seed; mount /data as a
# volume), and reused on every restart, so ids are stable and differ between
# installs. The M1 remembers registered ids, so keep that file: deleting it
# changes every device id. Setting device_seed (or ZB_DEVICE_SEED) overrides
# the file; `check` shows where the seed came from.
#device_seed = "abcd"
device_prefix = "02bb00" # 2-8 hex digits
# reserved_ips: addresses of REAL devices on your LAN that must never be used
# as a push IP (validation rejects them). Nothing is built in -- this list is
# yours. Same forms as pool (single IPs, ranges, CIDR). An M1's own address is
# always rejected on top of this, no need to list it.
# Example: a real Zapper Mini (say at 192.0.2.200) would steal the M1's TS
# pushes if we pointed a virtual tuner at it, so list it:
# reserved_ips = ["192.0.2.200"]
#
# check_free: before starting, send ONE ping and read the ARP/neighbour table
# for each planned push IP; if something already answers, fail (or just warn,
# see below) so two machines never fight over an address. Nothing is sent to
# an M1 or to reserved IPs, nothing is spoofed, and addresses this host
# already owns (e.g. aliases you added) are fine. On by default. Turn off with
# check_free = false, env ZB_CHECK_FREE=0, or `entrypoint.py check --skip-free-check`.
# check_free = true
# check_free_fail = true # false = only warn when an IP answers
# never_devs = [] # extra device ids to refuse
# ---------------------------------------------------------------------------
# 3. NETWORK MODE (must match the compose network you pick)
# host Linux only. Container shares the host NIC. Add each push IP as
# an alias on the host (printed by `check`, never run for you).
# macvlan Linux, wired Ethernet. Container gets its own LAN IPs. Wi-Fi
# and most hypervisor NICs drop macvlan -> use ipvlan.
# ipvlan Linux, L2 mode. Works on Wi-Fi; all IPs share the host MAC.
# Docker Desktop on macOS/Windows cannot do any of these: its VM hides the
# LAN. On a Mac keep running the bridge natively.
# ---------------------------------------------------------------------------
[network]
mode = "host"
# advertise_ip = "192.0.2.60" # REQUIRED for macvlan/ipvlan: the IP clients
# # use to reach the HDHR JSON/TS ports
# http_port = 80 # HDHR JSON (clients expect 80)
# ts_port = 5004
# ssdp = true # UPnP/SSDP announcements (so apps auto-find the HDHR).
# # false (or ZB_SSDP=0) = stay silent; add it in apps by IP.
# # Use false for test runs on a node/host that is not
# # meant to show up as a second HDHomeRun on the LAN.
# ---------------------------------------------------------------------------
# 4. WHAT CLIENTS SEE (HDHomeRun fidelity)
# ---------------------------------------------------------------------------
[hdhr]
mode = "per-m1" # "per-m1": one HDHR per M1 (works today)
# "merged": one HDHR, merged lineup, total TunerCount. PLAN-ONLY:
# `check` validates it, `run` refuses (docs/MERGED-LINEUP.md)
# device_id = "02BB0099" # merged mode: the one HDHR DeviceID (8 hex; derived if unset)
# tuner_cap = 4 # cap the TOTAL advertised TunerCount
hls_tuner = true # default for [[m1]].hls_tuner
# friendly_name = "ZapperBox M1 (emulated)"
# --- opt-in, off by default, RESERVED (accepted, ignored until shipped) ---
# discovery_responder = false # answer HDHR UDP 65001 discovery
# checksum_device_id = false # real-HDHR-style checksum DeviceID
# # (clients may rescan/duplicate: opt in only
# # on a fresh install)
# ---------------------------------------------------------------------------
# 4b. DISCOVERY (dry-run planner; nothing here sends anything)
# `docker compose run --rm zapperbridge discover --listen 60` listens
# passively on UDP 31064 (receive-only), reads each M1's tuner count from
# its announce, and prints the device ids + push IPs it WOULD register.
# A real Mini seen on the LAN is added to reserved_ips automatically.
# Sending an ANNOUNCE needs the explicit command-line flag `--apply`;
# no key in this file can turn it on.
# ---------------------------------------------------------------------------
[discovery]
# listen_seconds = 0 # default for `discover` (0 = configured M1s only)
# sweep = [] # e.g. ["192.0.2.0/24"]: TCP-connect check of
# # the BRP port only, no bytes sent; off by default
# ---------------------------------------------------------------------------
# 5. WEB UI (read-only status pages at http://<host>/ui, see docs/WEBUI.md)
# "off" (default) /ui does not exist.
# "readonly" GET-only dashboard on the same :80 server. Private-LAN source
# addresses only. Set env ZUI_TOKEN (>= 8 chars) to also require
# a token (Bearer header, or open /ui?token=... once to get a
# cookie). Never port-forward it. It listens wherever :80 does
# (all interfaces on host networking).
# "admin" RESERVED: accepted but nothing implements it.
# ---------------------------------------------------------------------------
[ui]
mode = "readonly" # "off", "readonly" or "admin"
# admin needs a token of >=16 chars in env ZUI_ADMIN_TOKEN (not in this file)
# ---------------------------------------------------------------------------
# 6. REQUEST LOG (JSON lines of stream open/close; path /data/zapperbridge-req.jsonl,
# override with env ZB_REQLOG_PATH; see docs/WEBUI.md)
# ---------------------------------------------------------------------------
[reqlog]
enabled = false
retention_days = 7
max_mb = 50
new logs:
# zapperbridge.toml -- ONE file for every choice the user has to make.
# Copy to ./zapperbridge.toml, edit, and mount it at /config/zapperbridge.toml
# (docker-compose.yml already does). Validate without starting anything:
# docker compose run --rm zapperbridge check
# Every key is optional; the defaults are the safe ones.
# ---------------------------------------------------------------------------
# 1. WHICH M1s? One [[m1]] block per ZapperBox M1 (or leave all out).
# On each M1: turn ON "sharing" in the Zapper app first, or it answers
# NOT_SHARED. Each M1 becomes its own virtual HDHomeRun with its own
# HDHR DeviceID, tuners, and block of device ids + push IPs.
# ---------------------------------------------------------------------------
# No [[m1]] block at all => one M1, auto-discovered from its UDP 31064
# announce (works with exactly one M1 on the LAN).
#
[[m1]]
name = "living-room"
address = "auto:acdbda698f08" # an IP, "auto", or "auto:<m1 device id>"
leases = 3 # BRP tuners to register, 1-3 (the M1 grants 3)
# # hls_tuner = true # also offer the M1's fixed HLS tuner (+1)
# # advanced, usually leave out (derived, stable across restarts):
device_id = "02BB0012" # HDHR DeviceID, 8 hex
# # devs = ["02bb00aaaaaa", "02bb00bbbbbb", "02bb00cccccc"]
push_ips = ["192.168.1.81", "192.168.1.82", "192.168.1.83"]
advertise_ip = "192.168.1.29" # this virtual HDHR's own IP (see [network])
# # friendly_name = "Living room M1"
#
# [[m1]] # a second M1: just add another block
# name = "garage"
# address = "auto:02bb00a00001" # pick this M1 out by its device id
# advertise_ip = "192.0.2.61" # give each extra M1 its own IP (macvlan/ipvlan)
# # Without its own advertise_ip, extra M1s get shifted HTTP/TS ports and
# # HDHR clients may only find the one on :80.
# ---------------------------------------------------------------------------
# 2. PUSH IPs. The M1 sends each tuner's video to the IP we announce for
# that tuner's device id, TCP :31061. Every tuner needs ITS OWN IP, and the
# container (or host) must really own it so it can answer ARP.
# Never use an M1's own address (always rejected), a real device's address
# (list those in reserved_ips below), or anything else already in use.
# ---------------------------------------------------------------------------
[push]
# A list, ranges, or CIDR. Need one IP per tuner (3 per M1) -- a block you
# have reserved outside your router's DHCP range.
# pool = ["192.0.2.240-249"] # e.g. ["192.0.2.240-249"]
# # e.g. ["192.0.2.240", "192.0.2.241-243"]
# device ids are derived from prefix + sha1(seed|m1|slot). Leave device_seed
# UNSET (recommended): on first run a random per-install seed is generated and
# saved in the data dir (/data/seed, or $ZB_DATA_DIR/seed; mount /data as a
# volume), and reused on every restart, so ids are stable and differ between
# installs. The M1 remembers registered ids, so keep that file: deleting it
# changes every device id. Setting device_seed (or ZB_DEVICE_SEED) overrides
# the file; `check` shows where the seed came from.
#device_seed = "abcd"
device_prefix = "02bb00" # 2-8 hex digits
# reserved_ips: addresses of REAL devices on your LAN that must never be used
# as a push IP (validation rejects them). Nothing is built in -- this list is
# yours. Same forms as pool (single IPs, ranges, CIDR). An M1's own address is
# always rejected on top of this, no need to list it.
# Example: a real Zapper Mini (say at 192.0.2.200) would steal the M1's TS
# pushes if we pointed a virtual tuner at it, so list it:
# reserved_ips = ["192.0.2.200"]
#
# check_free: before starting, send ONE ping and read the ARP/neighbour table
# for each planned push IP; if something already answers, fail (or just warn,
# see below) so two machines never fight over an address. Nothing is sent to
# an M1 or to reserved IPs, nothing is spoofed, and addresses this host
# already owns (e.g. aliases you added) are fine. On by default. Turn off with
# check_free = false, env ZB_CHECK_FREE=0, or `entrypoint.py check --skip-free-check`.
# check_free = true
# check_free_fail = true # false = only warn when an IP answers
# never_devs = [] # extra device ids to refuse
# ---------------------------------------------------------------------------
# 3. NETWORK MODE (must match the compose network you pick)
# host Linux only. Container shares the host NIC. Add each push IP as
# an alias on the host (printed by `check`, never run for you).
# macvlan Linux, wired Ethernet. Container gets its own LAN IPs. Wi-Fi
# and most hypervisor NICs drop macvlan -> use ipvlan.
# ipvlan Linux, L2 mode. Works on Wi-Fi; all IPs share the host MAC.
# Docker Desktop on macOS/Windows cannot do any of these: its VM hides the
# LAN. On a Mac keep running the bridge natively.
# ---------------------------------------------------------------------------
[network]
mode = "host"
# advertise_ip = "192.0.2.60" # REQUIRED for macvlan/ipvlan: the IP clients
# # use to reach the HDHR JSON/TS ports
# http_port = 80 # HDHR JSON (clients expect 80)
# ts_port = 5004
# ssdp = true # UPnP/SSDP announcements (so apps auto-find the HDHR).
# # false (or ZB_SSDP=0) = stay silent; add it in apps by IP.
# # Use false for test runs on a node/host that is not
# # meant to show up as a second HDHomeRun on the LAN.
# ---------------------------------------------------------------------------
# 4. WHAT CLIENTS SEE (HDHomeRun fidelity)
# ---------------------------------------------------------------------------
[hdhr]
mode = "per-m1" # "per-m1": one HDHR per M1 (works today)
# "merged": one HDHR, merged lineup, total TunerCount. PLAN-ONLY:
# `check` validates it, `run` refuses (docs/MERGED-LINEUP.md)
# device_id = "02BB0099" # merged mode: the one HDHR DeviceID (8 hex; derived if unset)
# tuner_cap = 4 # cap the TOTAL advertised TunerCount
hls_tuner = true # default for [[m1]].hls_tuner
# friendly_name = "ZapperBox M1 (emulated)"
# --- opt-in, off by default, RESERVED (accepted, ignored until shipped) ---
# discovery_responder = false # answer HDHR UDP 65001 discovery
# checksum_device_id = false # real-HDHR-style checksum DeviceID
# # (clients may rescan/duplicate: opt in only
# # on a fresh install)
# ---------------------------------------------------------------------------
# 4b. DISCOVERY (dry-run planner; nothing here sends anything)
# `docker compose run --rm zapperbridge discover --listen 60` listens
# passively on UDP 31064 (receive-only), reads each M1's tuner count from
# its announce, and prints the device ids + push IPs it WOULD register.
# A real Mini seen on the LAN is added to reserved_ips automatically.
# Sending an ANNOUNCE needs the explicit command-line flag `--apply`;
# no key in this file can turn it on.
# ---------------------------------------------------------------------------
[discovery]
# listen_seconds = 0 # default for `discover` (0 = configured M1s only)
# sweep = [] # e.g. ["192.0.2.0/24"]: TCP-connect check of
# # the BRP port only, no bytes sent; off by default
# ---------------------------------------------------------------------------
# 5. WEB UI (read-only status pages at http://<host>/ui, see docs/WEBUI.md)
# "off" (default) /ui does not exist.
# "readonly" GET-only dashboard on the same :80 server. Private-LAN source
# addresses only. Set env ZUI_TOKEN (>= 8 chars) to also require
# a token (Bearer header, or open /ui?token=... once to get a
# cookie). Never port-forward it. It listens wherever :80 does
# (all interfaces on host networking).
# "admin" RESERVED: accepted but nothing implements it.
# ---------------------------------------------------------------------------
[ui]
mode = "readonly" # "off", "readonly" or "admin"
# admin needs a token of >=16 chars in env ZUI_ADMIN_TOKEN (not in this file)
# ---------------------------------------------------------------------------
# 6. REQUEST LOG (JSON lines of stream open/close; path /data/zapperbridge-req.jsonl,
# override with env ZB_REQLOG_PATH; see docs/WEBUI.md)
# ---------------------------------------------------------------------------
[reqlog]
enabled = false
retention_days = 7
max_mb = 50
You turned on sharing on the quad
sharing is on
whoops posted toml by mistake see below:
WARN ui.mode = "readonly" without ZUI_TOKEN: the UI is open to every private-LAN address that can reach :80 (set env ZUI_TOKEN to require a token; never expose it to the internet)
WARN network.advertise_ip unset: the facade will pick the route-selected primary LAN IP
device id seed: persisted seed file /data/seed
network mode: host | advertised TunerCount total: 4
M1 living-room addr=auto:acdbda698f08 hdhr=02BB0012 tuners=4 http=80 ts=5004
dev 02bb0033c56a -> push 192.168.1.81:31061
dev 02bb00ea1c5e -> push 192.168.1.82:31061
dev 02bb00bb2603 -> push 192.168.1.83:31061
+ 1 fixed HLS tuner (not retunable)
host mode: add each push IP as an alias on the host NIC (not done for you):
sudo ip addr add 192.168.1.81/32 dev eth0 # arping -D -I eth0 -c 2 192.168.1.81 first
sudo ip addr add 192.168.1.82/32 dev eth0 # arping -D -I eth0 -c 2 192.168.1.82 first
sudo ip addr add 192.168.1.83/32 dev eth0 # arping -D -I eth0 -c 2 192.168.1.83 first
info: push ip 192.168.1.81: already assigned to this host
info: push ip 192.168.1.82: already assigned to this host
info: push ip 192.168.1.83: already assigned to this host
starting facade for living-room
push-ip: seed 02bb0033c56a -> 192.168.1.81 (ZPUSH_IPS)
push-ip: seed 02bb00bb2603 -> 192.168.1.83 (ZPUSH_IPS)
push-ip: seed 02bb00ea1c5e -> 192.168.1.82 (ZPUSH_IPS)
full-stream diagnostics: ts_analysis=on gap_ms=100 capture_seconds=0
m1 auto-discovery (auto:acdbda698f08): waiting for the M1 in the background; serving now. auto-discovery listens for the M1's UDP 31064 broadcast, which does not reach a Docker bridge network or another subnet/VLAN: use host or macvlan networking, or skip discovery by setting the M1 address (ZM1=<ip> for the bare facade, ZB_M1=<ip> or [[m1]] address = "<ip>" in the config)
HDHR emulator: JSON on :80 TS-redirect on :5004
m1 auto-discovery (auto:acdbda698f08): still waiting after 30s (M1 acdbda698f08 not seen); auto-discovery listens for the M1's UDP 31064 broadcast, which does not reach a Docker bridge network or another subnet/VLAN: use host or macvlan networking, or skip discovery by setting the M1 address (ZM1=<ip> for the bare facade, ZB_M1=<ip> or [[m1]] address = "<ip>" in the config). Serving /healthz and an empty lineup meanwhile.
m1 auto-discovery (auto:acdbda698f08): 192.168.1.54 dev=acdbda698f08 (matched acdbda698f08)
ssdp on :1900 group 239.255.255.250 if 192.168.1.29 location http://192.168.1.29/device.xml udn uuid:d5842096-00cb-5f2c-a3ae-35e8b7375c81
epg refresh fail: RuntimeError('lineup unavailable: api8 failed and no cache') (serving last-good=False)
lineup: M1 unreachable or lineup fetch failed; no cache, serving []
epg watch err: RuntimeError('lineup unavailable: api8 failed and no cache')
Thanks for checking sharing. Two quick things so we can see what the box is actually saying:
- With the container running, open http://192.168.1.29/health (or run
curl http://192.168.1.29/health) and post the "api8" section, especially "last_error". - From the host, run
nc -vz 192.168.1.54 31065and post the result.
logs below:
ok false
time 1792369703
api8
reachable false
last_ok_age_sec null
last_fail_age_sec 0.0JS:0
last_error "api8 FAIL on a0 2/3/4/5"
source null
lineup
channels 0
version null
tuner_count 4
m1
ip "192.168.1.54"
spec "auto:acdbda698f08"
dev "acdbda698f08"
source "auto: matched acdbda698f08"
brp
initialised false
leases 0
sessions_live 0
last_push_age_sec null
qdrops_total 0
qdrop_bursts_total 0
warnings []
hls_streams_live 0
ssdp
enabled true
ip "192.168.1.29"
port 1900
location "http://192.168.1.29/device.xml"
udn "uuid:d5842096-00cb-5f2c-a3ae-35e8b7375c81"
msearch_replies 79
notifies 6
epg
present false
fresh false
age_sec null
channels_with_programmes 0
programmes 0
refreshing false
last_error "lineup unavailable: api8 failed and no cache"
brp
initialised
and
nc -vz 192.168.1.54 31065
Connection to 192.168.1.54 31065 port [tcp/*] succeeded!
Thanks, that narrows it down a lot. Your network is fine (port 31065 connects), but the box is refusing the bridge, and /health shows the bridge never registered any tuners with it (leases 0). The bridge uses its own newly generated tuner IDs, and your ZapperBox doesn't know them yet.
Can you try the registration step by hand?
-
Stop the container.
-
Run
docker compose run --rm zapperbridge discover --listen 60 --apply. It listens for your box for 60 seconds, then sends it the registration for the bridge's tuner IDs. -
Start the container again and check http://192.168.1.29/health.
If "reachable" is still false, please post the discover output plus the new /health. This step hasn't been tested much on other people's boxes yet, so your results really help. I'm also going to make the log print the box's exact refusal reason, so we're not guessing.
health:
ok false
time 1792370667
api8
reachable false
last_ok_age_sec null
last_fail_age_sec null
last_error null
source null
lineup
channels 0
version null
tuner_count 4
m1
ip "auto:acdbda698f08"
spec "auto:acdbda698f08"
dev null
source "auto-pending"
brp
initialised false
leases 0
sessions_live 0
last_push_age_sec null
qdrops_total 0
qdrop_bursts_total 0
warnings []
hls_streams_live 0
ssdp
enabled false
epg
present false
fresh false
age_sec null
channels_with_programmes 0
programmes 0
refreshing false
last_error null
lineup
channels
output in shell:
user@channels:~/Documents/zapperbridge$ sudo docker compose run --rm zapperbridge discover --listen 60 --apply
Container zapperbridge-zapperbridge-run-bd42651558a5 Creating
Container zapperbridge-zapperbridge-run-bd42651558a5 Created
== discovered ==
m1 acdbda698f08 @ 192.168.1.54 model=ZBM1-gt698 share=3 tuners=4 src=announce
-> leases=3 [evidence] announced tuners=4 = 3 usable (announced - 1; the M1 keeps the 4th for itself) (matches the captured M1)
== plan (APPLY requested) ==
WARN ui.mode = "readonly" without ZUI_TOKEN: the UI is open to every private-LAN address that can reach :80 (set env ZUI_TOKEN to require a token; never expose it to the internet)
WARN network.advertise_ip unset: the facade will pick the route-selected primary LAN IP
device id seed: persisted seed file /data/seed
network mode: host | advertised TunerCount total: 4
M1 living-room addr=auto:acdbda698f08 hdhr=02BB0012 tuners=4 http=80 ts=5004
dev 02bb00c02a7c -> push 192.168.1.81:31061
dev 02bb005a1115 -> push 192.168.1.82:31061
dev 02bb0049d214 -> push 192.168.1.83:31061
- 1 fixed HLS tuner (not retunable)
host mode: add each push IP as an alias on the host NIC (not done for you):
sudo ip addr add 192.168.1.81/32 dev eth0 # arping -D -I eth0 -c 2 192.168.1.81 first
sudo ip addr add 192.168.1.82/32 dev eth0 # arping -D -I eth0 -c 2 192.168.1.82 first
sudo ip addr add 192.168.1.83/32 dev eth0 # arping -D -I eth0 -c 2 192.168.1.83 first
== would announce (UDP 31064, from each push IP) ==
living-room dev 02bb00c02a7c: ANNOUNCE ip=192.168.1.81 share=1 tuners=0, then facade bootstrap: api6 join for this id from 192.168.1.81 (not done by m1super)
living-room dev 02bb005a1115: ANNOUNCE ip=192.168.1.82 share=1 tuners=0, then facade bootstrap: api6 join for this id from 192.168.1.82 (not done by m1super)
living-room dev 02bb0049d214: ANNOUNCE ip=192.168.1.83 share=1 tuners=0, then facade bootstrap: api6 join for this id from 192.168.1.83 (not done by m1super)
Traceback (most recent call last):
File "/app/docker/entrypoint.py", line 118, in
sys.exit(main(sys.argv))
^^^^^^^^^^^^^^
File "/app/docker/entrypoint.py", line 41, in main
return m1super.main(["plan"] + argv[2:])
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/app/m1super.py", line 404, in main
res = apply_plan(plan, errs, apply=True)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/app/m1super.py", line 344, in apply_plan
send(ip, m1disco.build_announce(dev, ip, token=int(time.time()) & 0x7fffffff),
File "/app/m1super.py", line 310, in _udp_sender
s.sendto(data, (tgt, port))
socket.gaierror: [Errno -2] Name or service not known
logs:
WARN ui.mode = "readonly" without ZUI_TOKEN: the UI is open to every private-LAN address that can reach :80 (set env ZUI_TOKEN to require a token; never expose it to the internet)
WARN network.advertise_ip unset: the facade will pick the route-selected primary LAN IP
device id seed: persisted seed file /data/seed
network mode: host | advertised TunerCount total: 4
M1 living-room addr=auto:acdbda698f08 hdhr=02BB0012 tuners=4 http=80 ts=5004
dev 02bb0033c56a -> push 192.168.1.81:31061
dev 02bb00ea1c5e -> push 192.168.1.82:31061
dev 02bb00bb2603 -> push 192.168.1.83:31061
+ 1 fixed HLS tuner (not retunable)
host mode: add each push IP as an alias on the host NIC (not done for you):
sudo ip addr add 192.168.1.81/32 dev eth0 # arping -D -I eth0 -c 2 192.168.1.81 first
sudo ip addr add 192.168.1.82/32 dev eth0 # arping -D -I eth0 -c 2 192.168.1.82 first
sudo ip addr add 192.168.1.83/32 dev eth0 # arping -D -I eth0 -c 2 192.168.1.83 first
info: push ip 192.168.1.81: already assigned to this host
info: push ip 192.168.1.82: already assigned to this host
info: push ip 192.168.1.83: already assigned to this host
starting facade for living-room
push-ip: seed 02bb0033c56a -> 192.168.1.81 (ZPUSH_IPS)
push-ip: seed 02bb00bb2603 -> 192.168.1.83 (ZPUSH_IPS)
push-ip: seed 02bb00ea1c5e -> 192.168.1.82 (ZPUSH_IPS)
full-stream diagnostics: ts_analysis=on gap_ms=100 capture_seconds=0
m1 auto-discovery (auto:acdbda698f08): waiting for the M1 in the background; serving now. auto-discovery listens for the M1's UDP 31064 broadcast, which does not reach a Docker bridge network or another subnet/VLAN: use host or macvlan networking, or skip discovery by setting the M1 address (ZM1=<ip> for the bare facade, ZB_M1=<ip> or [[m1]] address = "<ip>" in the config)
HDHR emulator: JSON on :80 TS-redirect on :5004
m1 auto-discovery (auto:acdbda698f08): still waiting after 30s (M1 acdbda698f08 not seen); auto-discovery listens for the M1's UDP 31064 broadcast, which does not reach a Docker bridge network or another subnet/VLAN: use host or macvlan networking, or skip discovery by setting the M1 address (ZM1=<ip> for the bare facade, ZB_M1=<ip> or [[m1]] address = "<ip>" in the config). Serving /healthz and an empty lineup meanwhile.
m1 auto-discovery (auto:acdbda698f08): 192.168.1.54 dev=acdbda698f08 (matched acdbda698f08)
ssdp on :1900 group 239.255.255.250 if 192.168.1.29 location http://192.168.1.29/device.xml udn uuid:d5842096-00cb-5f2c-a3ae-35e8b7375c81
epg refresh fail: RuntimeError('lineup unavailable: api8 failed and no cache') (serving last-good=False)
lineup: M1 unreachable or lineup fetch failed; no cache, serving []
epg watch err: RuntimeError('lineup unavailable: api8 failed and no cache')
lineup: M1 unreachable or lineup fetch failed; no cache, serving []
lineup: M1 unreachable or lineup fetch failed; no cache, serving []
epg refresh fail: RuntimeError('lineup unavailable: api8 failed and no cache') (serving last-good=False)
epg watch err: RuntimeError('lineup unavailable: api8 failed and no cache')
lineup: M1 unreachable or lineup fetch failed; no cache, serving []
lineup: M1 unreachable or lineup fetch failed; no cache, serving []
epg refresh fail: RuntimeError('lineup unavailable: api8 failed and no cache') (serving last-good=False)
epg watch err: RuntimeError('lineup unavailable: api8 failed and no cache')
lineup: M1 unreachable or lineup fetch failed; no cache, serving []
lineup: M1 unreachable or lineup fetch failed; no cache, serving []
lineup: M1 unreachable or lineup fetch failed; no cache, serving []
epg refresh fail: RuntimeError('lineup unavailable: api8 failed and no cache') (serving last-good=False)
epg watch err: RuntimeError('lineup unavailable: api8 failed and no cache')
lineup: M1 unreachable or lineup fetch failed; no cache, serving []
lineup: M1 unreachable or lineup fetch failed; no cache, serving []
epg refresh fail: RuntimeError('lineup unavailable: api8 failed and no cache') (serving last-good=False)
epg watch err: RuntimeError('lineup unavailable: api8 failed and no cache')
lineup: M1 unreachable or lineup fetch failed; no cache, serving []
signal 15: shutting down
ssdp byebye sent
WARN ui.mode = "readonly" without ZUI_TOKEN: the UI is open to every private-LAN address that can reach :80 (set env ZUI_TOKEN to require a token; never expose it to the internet)
WARN network.advertise_ip unset: the facade will pick the route-selected primary LAN IP
device id seed: persisted seed file /data/seed
network mode: host | advertised TunerCount total: 4
M1 living-room addr=auto:acdbda698f08 hdhr=02BB0012 tuners=4 http=80 ts=5004
dev 02bb0033c56a -> push 192.168.1.81:31061
dev 02bb00ea1c5e -> push 192.168.1.82:31061
dev 02bb00bb2603 -> push 192.168.1.83:31061
+ 1 fixed HLS tuner (not retunable)
host mode: add each push IP as an alias on the host NIC (not done for you):
sudo ip addr add 192.168.1.81/32 dev eth0 # arping -D -I eth0 -c 2 192.168.1.81 first
sudo ip addr add 192.168.1.82/32 dev eth0 # arping -D -I eth0 -c 2 192.168.1.82 first
sudo ip addr add 192.168.1.83/32 dev eth0 # arping -D -I eth0 -c 2 192.168.1.83 first
info: push ip 192.168.1.81: already assigned to this host
info: push ip 192.168.1.82: already assigned to this host
info: push ip 192.168.1.83: already assigned to this host
starting facade for living-room
push-ip: seed 02bb0033c56a -> 192.168.1.81 (ZPUSH_IPS)
push-ip: seed 02bb00bb2603 -> 192.168.1.83 (ZPUSH_IPS)
push-ip: seed 02bb00ea1c5e -> 192.168.1.82 (ZPUSH_IPS)
full-stream diagnostics: ts_analysis=on gap_ms=100 capture_seconds=0
m1 auto-discovery (auto:acdbda698f08): waiting for the M1 in the background; serving now. auto-discovery listens for the M1's UDP 31064 broadcast, which does not reach a Docker bridge network or another subnet/VLAN: use host or macvlan networking, or skip discovery by setting the M1 address (ZM1=<ip> for the bare facade, ZB_M1=<ip> or [[m1]] address = "<ip>" in the config)
HDHR emulator: JSON on :80 TS-redirect on :5004
m1 auto-discovery (auto:acdbda698f08): 192.168.1.54 dev=acdbda698f08 (matched acdbda698f08)
ssdp on :1900 group 239.255.255.250 if 192.168.1.29 location http://192.168.1.29/device.xml udn uuid:d5842096-00cb-5f2c-a3ae-35e8b7375c81
epg refresh fail: RuntimeError('lineup unavailable: api8 failed and no cache') (serving last-good=False)
epg watch err: RuntimeError('lineup unavailable: api8 failed and no cache')
lineup: M1 unreachable or lineup fetch failed; no cache, serving []
signal 15: shutting down
ssdp byebye sent
WARN ui.mode = "readonly" without ZUI_TOKEN: the UI is open to every private-LAN address that can reach :80 (set env ZUI_TOKEN to require a token; never expose it to the internet)
WARN network.advertise_ip unset: the facade will pick the route-selected primary LAN IP
device id seed: persisted seed file /data/seed
network mode: host | advertised TunerCount total: 4
M1 living-room addr=auto:acdbda698f08 hdhr=02BB0012 tuners=4 http=80 ts=5004
dev 02bb0033c56a -> push 192.168.1.81:31061
dev 02bb00ea1c5e -> push 192.168.1.82:31061
dev 02bb00bb2603 -> push 192.168.1.83:31061
+ 1 fixed HLS tuner (not retunable)
host mode: add each push IP as an alias on the host NIC (not done for you):
sudo ip addr add 192.168.1.81/32 dev eth0 # arping -D -I eth0 -c 2 192.168.1.81 first
sudo ip addr add 192.168.1.82/32 dev eth0 # arping -D -I eth0 -c 2 192.168.1.82 first
sudo ip addr add 192.168.1.83/32 dev eth0 # arping -D -I eth0 -c 2 192.168.1.83 first
info: push ip 192.168.1.81: already assigned to this host
info: push ip 192.168.1.82: already assigned to this host
info: push ip 192.168.1.83: already assigned to this host
starting facade for living-room
push-ip: seed 02bb0033c56a -> 192.168.1.81 (ZPUSH_IPS)
push-ip: seed 02bb00bb2603 -> 192.168.1.83 (ZPUSH_IPS)
push-ip: seed 02bb00ea1c5e -> 192.168.1.82 (ZPUSH_IPS)
full-stream diagnostics: ts_analysis=on gap_ms=100 capture_seconds=0
m1 auto-discovery (auto:acdbda698f08): waiting for the M1 in the background; serving now. auto-discovery listens for the M1's UDP 31064 broadcast, which does not reach a Docker bridge network or another subnet/VLAN: use host or macvlan networking, or skip discovery by setting the M1 address (ZM1=<ip> for the bare facade, ZB_M1=<ip> or [[m1]] address = "<ip>" in the config)
HDHR emulator: JSON on :80 TS-redirect on :5004
m1 auto-discovery (auto:acdbda698f08): 192.168.1.54 dev=acdbda698f08 (matched acdbda698f08)
ssdp on :1900 group 239.255.255.250 if 192.168.1.29 location http://192.168.1.29/device.xml udn uuid:d5842096-00cb-5f2c-a3ae-35e8b7375c81
epg refresh fail: RuntimeError('lineup unavailable: api8 failed and no cache') (serving last-good=False)
epg watch err: RuntimeError('lineup unavailable: api8 failed and no cache')
signal 15: shutting down
ssdp byebye sent
WARN ui.mode = "readonly" without ZUI_TOKEN: the UI is open to every private-LAN address that can reach :80 (set env ZUI_TOKEN to require a token; never expose it to the internet)
WARN network.advertise_ip unset: the facade will pick the route-selected primary LAN IP
device id seed: persisted seed file /data/seed
network mode: host | advertised TunerCount total: 4
M1 living-room addr=auto:acdbda698f08 hdhr=02BB0012 tuners=4 http=80 ts=5004
dev 02bb0033c56a -> push 192.168.1.81:31061
dev 02bb00ea1c5e -> push 192.168.1.82:31061
dev 02bb00bb2603 -> push 192.168.1.83:31061
+ 1 fixed HLS tuner (not retunable)
host mode: add each push IP as an alias on the host NIC (not done for you):
sudo ip addr add 192.168.1.81/32 dev eth0 # arping -D -I eth0 -c 2 192.168.1.81 first
sudo ip addr add 192.168.1.82/32 dev eth0 # arping -D -I eth0 -c 2 192.168.1.82 first
sudo ip addr add 192.168.1.83/32 dev eth0 # arping -D -I eth0 -c 2 192.168.1.83 first
info: push ip 192.168.1.81: already assigned to this host
info: push ip 192.168.1.82: already assigned to this host
info: push ip 192.168.1.83: already assigned to this host
starting facade for living-room
push-ip: seed 02bb0033c56a -> 192.168.1.81 (ZPUSH_IPS)
push-ip: seed 02bb00bb2603 -> 192.168.1.83 (ZPUSH_IPS)
push-ip: seed 02bb00ea1c5e -> 192.168.1.82 (ZPUSH_IPS)
full-stream diagnostics: ts_analysis=on gap_ms=100 capture_seconds=0
m1 auto-discovery (auto:acdbda698f08): waiting for the M1 in the background; serving now. auto-discovery listens for the M1's UDP 31064 broadcast, which does not reach a Docker bridge network or another subnet/VLAN: use host or macvlan networking, or skip discovery by setting the M1 address (ZM1=<ip> for the bare facade, ZB_M1=<ip> or [[m1]] address = "<ip>" in the config)
HDHR emulator: JSON on :80 TS-redirect on :5004
m1 auto-discovery (auto:acdbda698f08): still waiting after 30s (M1 acdbda698f08 not seen); auto-discovery listens for the M1's UDP 31064 broadcast, which does not reach a Docker bridge network or another subnet/VLAN: use host or macvlan networking, or skip discovery by setting the M1 address (ZM1=<ip> for the bare facade, ZB_M1=<ip> or [[m1]] address = "<ip>" in the config). Serving /healthz and an empty lineup meanwhile.
m1 auto-discovery (auto:acdbda698f08): 192.168.1.54 dev=acdbda698f08 (matched acdbda698f08)
ssdp on :1900 group 239.255.255.250 if 192.168.1.29 location http://192.168.1.29/device.xml udn uuid:d5842096-00cb-5f2c-a3ae-35e8b7375c81
epg refresh fail: RuntimeError('lineup unavailable: api8 failed and no cache') (serving last-good=False)
lineup: M1 unreachable or lineup fetch failed; no cache, serving []
epg watch err: RuntimeError('lineup unavailable: api8 failed and no cache')
new health:
ok false
time 1792370806
api8
reachable false
last_ok_age_sec null
last_fail_age_sec 0.0JS:0
last_error "api8 FAIL on a0 2/3/4/5"
source null
lineup
channels 0
version null
tuner_count 4
m1
ip "192.168.1.54"
spec "auto:acdbda698f08"
dev "acdbda698f08"
source "auto: matched acdbda698f08"
brp
initialised false
leases 0
sessions_live 0
last_push_age_sec null
qdrops_total 0
qdrop_bursts_total 0
warnings []
hls_streams_live 0
ssdp
enabled true
ip "192.168.1.29"
port 1900
location "http://192.168.1.29/device.xml"
udn "uuid:d5842096-00cb-5f2c-a3ae-35e8b7375c81"
msearch_replies 9
notifies 3
epg
present false
fresh false
age_sec null
channels_with_programmes 0
programmes 0
refreshing false
last_error "lineup unavailable: api8 failed and no cache"
Thanks, that output found two bugs on our side: the registration step tried to send to "auto:…" instead of your M1's IP, so nothing was sent. Sorry about that. Workaround for the first one:
- In zapperbridge.toml set
address = "192.168.1.54"(instead of the auto: value).- Run
docker compose run --rm zapperbridge discover --listen 60 --applyagain.- Compare the "dev 02bb00…" IDs it prints with the ones in the container's startup log. If they match, start the container and check /health. If they don't match (which is the second bug), post both and hold off. I'm fixing that so both use the same IDs.
they are different see below:
shell:
user@channels:~/Documents/zapperbridge$ sudo docker compose run --rm zapperbridge discover --listen 60 --apply
Container zapperbridge-zapperbridge-run-c0f8082e2c7e Creating
Container zapperbridge-zapperbridge-run-c0f8082e2c7e Created
== discovered ==
m1 acdbda698f08 @ 192.168.1.54 model=ZBM1-gt698 share=3 tuners=4 src=announce
-> leases=3 [evidence] announced tuners=4 = 3 usable (announced - 1; the M1 keeps the 4th for itself) (matches the captured M1)
== plan (APPLY requested) ==
WARN ui.mode = "readonly" without ZUI_TOKEN: the UI is open to every private-LAN address that can reach :80 (set env ZUI_TOKEN to require a token; never expose it to the internet)
WARN network.advertise_ip unset: the facade will pick the route-selected primary LAN IP
device id seed: persisted seed file /data/seed
network mode: host | advertised TunerCount total: 4
M1 living-room addr=192.168.1.54 hdhr=02BB0012 tuners=4 http=80 ts=5004
dev 02bb00d99dc7 -> push 192.168.1.81:31061
dev 02bb005354ad -> push 192.168.1.82:31061
dev 02bb00f59838 -> push 192.168.1.83:31061
+ 1 fixed HLS tuner (not retunable)
host mode: add each push IP as an alias on the host NIC (not done for you):
sudo ip addr add 192.168.1.81/32 dev eth0 # arping -D -I eth0 -c 2 192.168.1.81 first
sudo ip addr add 192.168.1.82/32 dev eth0 # arping -D -I eth0 -c 2 192.168.1.82 first
sudo ip addr add 192.168.1.83/32 dev eth0 # arping -D -I eth0 -c 2 192.168.1.83 first
== would announce (UDP 31064, from each push IP) ==
living-room dev 02bb00d99dc7: ANNOUNCE ip=192.168.1.81 share=1 tuners=0, then facade bootstrap: api6 join for this id from 192.168.1.81 (not done by m1super)
living-room dev 02bb005354ad: ANNOUNCE ip=192.168.1.82 share=1 tuners=0, then facade bootstrap: api6 join for this id from 192.168.1.82 (not done by m1super)
living-room dev 02bb00f59838: ANNOUNCE ip=192.168.1.83 share=1 tuners=0, then facade bootstrap: api6 join for this id from 192.168.1.83 (not done by m1super)
announced 02bb00d99dc7 from 192.168.1.81
announced 02bb005354ad from 192.168.1.82
announced 02bb00f59838 from 192.168.1.83
user@channels:~/Documents/zapperbridge$
logs:
WARN ui.mode = "readonly" without ZUI_TOKEN: the UI is open to every private-LAN address that can reach :80 (set env ZUI_TOKEN to require a token; never expose it to the internet)
WARN network.advertise_ip unset: the facade will pick the route-selected primary LAN IP
device id seed: persisted seed file /data/seed
network mode: host | advertised TunerCount total: 4
M1 living-room addr=192.168.1.54 hdhr=02BB0012 tuners=4 http=80 ts=5004
dev 02bb00a0735a -> push 192.168.1.81:31061
dev 02bb003c6c0a -> push 192.168.1.82:31061
dev 02bb00c7fbb9 -> push 192.168.1.83:31061
+ 1 fixed HLS tuner (not retunable)
host mode: add each push IP as an alias on the host NIC (not done for you):
sudo ip addr add 192.168.1.81/32 dev eth0 # arping -D -I eth0 -c 2 192.168.1.81 first
sudo ip addr add 192.168.1.82/32 dev eth0 # arping -D -I eth0 -c 2 192.168.1.82 first
sudo ip addr add 192.168.1.83/32 dev eth0 # arping -D -I eth0 -c 2 192.168.1.83 first
info: push ip 192.168.1.81: already assigned to this host
info: push ip 192.168.1.82: already assigned to this host
info: push ip 192.168.1.83: already assigned to this host
starting facade for living-room
push-ip: seed 02bb003c6c0a -> 192.168.1.82 (ZPUSH_IPS)
push-ip: seed 02bb00a0735a -> 192.168.1.81 (ZPUSH_IPS)
push-ip: seed 02bb00c7fbb9 -> 192.168.1.83 (ZPUSH_IPS)
full-stream diagnostics: ts_analysis=on gap_ms=100 capture_seconds=0
ssdp on :1900 group 239.255.255.250 if 192.168.1.29 location http://192.168.1.29/device.xml udn uuid:d5842096-00cb-5f2c-a3ae-35e8b7375c81
HDHR emulator: JSON on :80 TS-redirect on :5004
signal 15: shutting down
ssdp byebye sent
WARN ui.mode = "readonly" without ZUI_TOKEN: the UI is open to every private-LAN address that can reach :80 (set env ZUI_TOKEN to require a token; never expose it to the internet)
WARN network.advertise_ip unset: the facade will pick the route-selected primary LAN IP
device id seed: persisted seed file /data/seed
network mode: host | advertised TunerCount total: 4
M1 living-room addr=192.168.1.54 hdhr=02BB0012 tuners=4 http=80 ts=5004
dev 02bb00a0735a -> push 192.168.1.81:31061
dev 02bb003c6c0a -> push 192.168.1.82:31061
dev 02bb00c7fbb9 -> push 192.168.1.83:31061
+ 1 fixed HLS tuner (not retunable)
host mode: add each push IP as an alias on the host NIC (not done for you):
sudo ip addr add 192.168.1.81/32 dev eth0 # arping -D -I eth0 -c 2 192.168.1.81 first
sudo ip addr add 192.168.1.82/32 dev eth0 # arping -D -I eth0 -c 2 192.168.1.82 first
sudo ip addr add 192.168.1.83/32 dev eth0 # arping -D -I eth0 -c 2 192.168.1.83 first
info: push ip 192.168.1.81: already assigned to this host
info: push ip 192.168.1.82: already assigned to this host
info: push ip 192.168.1.83: already assigned to this host
starting facade for living-room
push-ip: seed 02bb003c6c0a -> 192.168.1.82 (ZPUSH_IPS)
push-ip: seed 02bb00a0735a -> 192.168.1.81 (ZPUSH_IPS)
push-ip: seed 02bb00c7fbb9 -> 192.168.1.83 (ZPUSH_IPS)
full-stream diagnostics: ts_analysis=on gap_ms=100 capture_seconds=0
ssdp on :1900 group 239.255.255.250 if 192.168.1.29 location http://192.168.1.29/device.xml udn uuid:d5842096-00cb-5f2c-a3ae-35e8b7375c81
HDHR emulator: JSON on :80 TS-redirect on :5004
lineup: M1 unreachable or lineup fetch failed; no cache, serving []
epg refresh fail: RuntimeError('lineup unavailable: api8 failed and no cache') (serving last-good=False)
epg watch err: RuntimeError('lineup unavailable: api8 failed and no cache')
lineup: M1 unreachable or lineup fetch failed; no cache, serving []
lineup: M1 unreachable or lineup fetch failed; no cache, serving []