diff --git a/components/ws_bridge/README.md b/components/ws_bridge/README.md index ca67594e..b74b926a 100644 --- a/components/ws_bridge/README.md +++ b/components/ws_bridge/README.md @@ -534,6 +534,7 @@ ws_bridge: |--------|:--------:|-------------| | `unique_id` / `name` | ✓ | Same as every other platform | | `latitude` / `longitude` | ✓ | Float or `!lambda`. Return `NAN` when there's no fix yet — Home Assistant then shows the tracker as unavailable rather than pinning it at 0,0 | +| `report_unknown` | | Default `true`. When `latitude`/`longitude` are `NAN`, send `"unknown"` so Home Assistant marks the tracker unavailable. Set `false` to skip the update and leave the last reported coordinates in place | | `gps_accuracy` | | Float or `!lambda`, meters. Home Assistant uses it when deciding zone membership | | `icon` | | Same as every other platform | | `update_interval` | | Default `60s` | diff --git a/components/ws_bridge/__init__.py b/components/ws_bridge/__init__.py index 4a8291a1..43f164a7 100644 --- a/components/ws_bridge/__init__.py +++ b/components/ws_bridge/__init__.py @@ -52,6 +52,7 @@ CONF_ON_DECLARE, CONF_TRACKERS, CONF_GPS_ACCURACY, + CONF_REPORT_UNKNOWN, CONF_PING_INTERVAL, CONF_PONG_TIMEOUT, CONF_RECONNECT_TIMEOUT, @@ -90,6 +91,7 @@ cv.Required(CONF_LATITUDE): cv.templatable(cv.float_), cv.Required(CONF_LONGITUDE): cv.templatable(cv.float_), cv.Optional(CONF_GPS_ACCURACY): cv.templatable(cv.float_), + cv.Optional(CONF_REPORT_UNKNOWN, default=True): cv.boolean, } ).extend(cv.polling_component_schema("60s")) @@ -350,6 +352,7 @@ async def to_code(config): if CONF_GPS_ACCURACY in conf: acc_template = await cg.templatable(conf[CONF_GPS_ACCURACY], [], cg.float_) cg.add(tracker.set_gps_accuracy(acc_template)) + cg.add(tracker.set_report_unknown(conf[CONF_REPORT_UNKNOWN])) for conf in config.get(CONF_ENTITIES, []): await to_code_entity_ref(var, conf) diff --git a/components/ws_bridge/const.py b/components/ws_bridge/const.py index 9a84b82f..29b1b9e2 100644 --- a/components/ws_bridge/const.py +++ b/components/ws_bridge/const.py @@ -23,6 +23,7 @@ CONF_TRACKERS = "trackers" CONF_GPS_ACCURACY = "gps_accuracy" +CONF_REPORT_UNKNOWN = "report_unknown" CONF_UPDATE_ID = "update_id" CONF_BUTTON_ID = "button_id" CONF_SENSOR_ID = "sensor_id" diff --git a/components/ws_bridge/ws_bridge_tracker.cpp b/components/ws_bridge/ws_bridge_tracker.cpp index 5559a117..c6c2e7e5 100644 --- a/components/ws_bridge/ws_bridge_tracker.cpp +++ b/components/ws_bridge/ws_bridge_tracker.cpp @@ -28,12 +28,15 @@ void WsBridgeTracker::ws_bridge_declare() { void WsBridgeTracker::publish_position_() { float lat = this->latitude_.value(); float lon = this->longitude_.value(); - // No fix yet, or the source sensor has no state. Report the position as - // explicitly unknown rather than sending the coordinates anyway: a NaN that - // serializes badly, or a zeroed pair, would put the device at 0,0 — a spot - // in the Atlantic that Home Assistant can't tell apart from a real reading. + // No fix yet, or the source sensor has no state. Never send the coordinates + // anyway: a NaN that serializes badly, or a zeroed pair, would put the + // device at 0,0 — a spot in the Atlantic that Home Assistant can't tell + // apart from a real reading. When report_unknown_ is true, send "unknown" so + // HA marks the tracker unavailable; otherwise skip the update and leave the + // last reported coordinates in place. if (std::isnan(lat) || std::isnan(lon)) { - this->parent_->send_state_string(this->unique_id_, "unknown"); + if (this->report_unknown_) + this->parent_->send_state_string(this->unique_id_, "unknown"); return; } this->parent_->send_state_object(this->unique_id_, [this, lat, lon](JsonObject value) { diff --git a/components/ws_bridge/ws_bridge_tracker.h b/components/ws_bridge/ws_bridge_tracker.h index 946c1828..d0862971 100644 --- a/components/ws_bridge/ws_bridge_tracker.h +++ b/components/ws_bridge/ws_bridge_tracker.h @@ -25,6 +25,7 @@ class WsBridgeTracker : public PollingComponent, public WsBridgeDevice { this->gps_accuracy_ = v; this->has_gps_accuracy_ = true; } + void set_report_unknown(bool report_unknown) { this->report_unknown_ = report_unknown; } void dump_config() override; void update() override; @@ -39,6 +40,7 @@ class WsBridgeTracker : public PollingComponent, public WsBridgeDevice { TemplatableValue longitude_{}; TemplatableValue gps_accuracy_{}; bool has_gps_accuracy_{false}; + bool report_unknown_{true}; }; } // namespace ws_bridge diff --git a/tests/components/ws_bridge/test.esp32-idf.yaml b/tests/components/ws_bridge/test.esp32-idf.yaml index b9cd9ae4..306b2252 100644 --- a/tests/components/ws_bridge/test.esp32-idf.yaml +++ b/tests/components/ws_bridge/test.esp32-idf.yaml @@ -49,6 +49,12 @@ ws_bridge: longitude: !lambda return 126.9780; gps_accuracy: 8 update_interval: 30s + - unique_id: car_location_stale + name: "Car Location (keep last fix)" + latitude: !lambda return NAN; + longitude: !lambda return NAN; + report_unknown: false + update_interval: 30s # Exposes existing, locally-declared entities (below) without a parallel # `platform: ws_bridge` entity per value — see the README's "Exposing # existing entities" section. unique_id/name default to the source's own