@@ -9,6 +9,9 @@ defmodule TeslaMate.Mqtt.PubSub.VehicleSubscriber do
99 alias TeslaMate.Vehicles.Vehicle.Summary
1010 alias TeslaMate.Vehicles
1111
12+ @ discovery_retry_initial_delay :timer . seconds ( 5 )
13+ @ discovery_retry_max_delay :timer . minutes ( 5 )
14+
1215 defstruct [
1316 :car_id ,
1417 :last_values ,
@@ -17,7 +20,12 @@ defmodule TeslaMate.Mqtt.PubSub.VehicleSubscriber do
1720 :discovery ,
1821 :discovery_base_url ,
1922 :discovery_prefix ,
20- :discovery_device
23+ :discovery_device ,
24+ :discovery_pending_summary ,
25+ :discovery_pending_device ,
26+ :discovery_retry_delay ,
27+ :discovery_retry_timer ,
28+ :discovery_retry_token
2129 ]
2230
2331 alias __MODULE__ , as: State
@@ -126,33 +134,104 @@ defmodule TeslaMate.Mqtt.PubSub.VehicleSubscriber do
126134 { :noreply , % { state | last_values: last_values } }
127135 end
128136
137+ def handle_info (
138+ { :retry_discovery , token } ,
139+ % State {
140+ discovery: true ,
141+ discovery_retry_token: token ,
142+ discovery_pending_summary: % Summary { } = summary ,
143+ discovery_pending_device: device
144+ } = state
145+ ) do
146+ state = % {
147+ state
148+ | discovery_pending_summary: nil ,
149+ discovery_pending_device: nil ,
150+ discovery_retry_timer: nil ,
151+ discovery_retry_token: nil
152+ }
153+
154+ { :noreply , publish_or_schedule_discovery ( summary , device , state ) }
155+ end
156+
157+ def handle_info ( { :retry_discovery , _stale_token } , % State { } = state ) , do: { :noreply , state }
158+
129159 defp maybe_publish_discovery ( % Summary { } = summary , % State { discovery: true } = state ) do
130160 opts = discovery_opts ( state )
131161 device = HomeAssistant . device ( summary , opts )
132162
133- if device == state . discovery_device do
134- state
135- else
136- case publish_discovery ( summary , opts , state ) do
137- :ok -> % { state | discovery_device: device }
138- { :error , _reason } -> state
139- end
163+ cond do
164+ device == state . discovery_device ->
165+ reset_discovery_retry ( state )
166+
167+ is_reference ( state . discovery_retry_timer ) ->
168+ % {
169+ state
170+ | discovery_pending_summary: summary ,
171+ discovery_pending_device: device
172+ }
173+
174+ true ->
175+ publish_or_schedule_discovery ( summary , device , state )
140176 end
141177 end
142178
143179 defp maybe_publish_discovery ( % Summary { } , % State { } = state ) , do: state
144180
145- defp publish_discovery ( % Summary { } = summary , opts , % State { deps: deps } ) do
146- case HomeAssistant . publish ( summary , opts , deps . publisher ) do
181+ defp publish_or_schedule_discovery ( % Summary { } = summary , device , % State { } = state ) do
182+ case publish_discovery ( summary , discovery_opts ( state ) , state ) do
147183 :ok ->
148- :ok
184+ state
185+ |> reset_discovery_retry ( )
186+ |> Map . put ( :discovery_device , device )
149187
150- { :error , reason } = error ->
151- Logger . warning ( "MQTT HA discovery publishing failed: #{ inspect ( reason ) } " )
152- error
188+ { :error , reason } ->
189+ schedule_discovery_retry ( summary , device , reason , state )
153190 end
154191 end
155192
193+ defp publish_discovery ( % Summary { } = summary , opts , % State { deps: deps } ) do
194+ HomeAssistant . publish ( summary , opts , deps . publisher )
195+ end
196+
197+ defp schedule_discovery_retry ( % Summary { } = summary , device , reason , % State { } = state ) do
198+ delay = next_discovery_retry_delay ( state . discovery_retry_delay )
199+ token = make_ref ( )
200+ timer = Process . send_after ( self ( ) , { :retry_discovery , token } , delay )
201+
202+ Logger . warning (
203+ "MQTT HA discovery publishing failed: #{ inspect ( reason ) } ; retrying in #{ div ( delay , 1_000 ) } s"
204+ )
205+
206+ % {
207+ state
208+ | discovery_pending_summary: summary ,
209+ discovery_pending_device: device ,
210+ discovery_retry_delay: delay ,
211+ discovery_retry_timer: timer ,
212+ discovery_retry_token: token
213+ }
214+ end
215+
216+ defp next_discovery_retry_delay ( nil ) , do: @ discovery_retry_initial_delay
217+
218+ defp next_discovery_retry_delay ( delay ) do
219+ min ( delay * 2 , @ discovery_retry_max_delay )
220+ end
221+
222+ defp reset_discovery_retry ( % State { discovery_retry_timer: timer } = state ) do
223+ if is_reference ( timer ) , do: Process . cancel_timer ( timer )
224+
225+ % {
226+ state
227+ | discovery_pending_summary: nil ,
228+ discovery_pending_device: nil ,
229+ discovery_retry_delay: nil ,
230+ discovery_retry_timer: nil ,
231+ discovery_retry_token: nil
232+ }
233+ end
234+
156235 defp discovery_opts ( % State { } = state ) do
157236 [
158237 car_id: state . car_id ,
0 commit comments