Broadcast a message
Closing early, a system outage, a shift change: tell everyone holding a device at once.
Needs: a service account with ViewDevices and SendMessages, and .NET 10.
OHR_TOKEN=ohrs_… dotnet run broadcast-message.cs -- "Downtown store" "We close in 15 minutes."Sent to 14 device(s).#:package OhrTech.Management@*// Shows a message on every device in a fleet and the fleets below it.// OHR_TOKEN=ohrs_… dotnet run broadcast-message.cs -- "Downtown store" "We close in 15 minutes."// Needs ViewDevices and SendMessages. A device that is offline shows it when it next checks in.using OhrTech.Management;
if (args.Length < 2){ Console.Error.WriteLine("Usage: broadcast-message.cs <fleet name> <message>"); return 2;}var (fleetName, text) = (args[0], args[1]);
using var api = OhrManagementClient.ForServiceAccount( Environment.GetEnvironmentVariable("OHR_TOKEN") ?? throw new InvalidOperationException("Set OHR_TOKEN to a service account token."));
var fleets = await api.ListFleetsAsync();var root = fleets.SingleOrDefault(f => string.Equals(f.FleetName, fleetName, StringComparison.OrdinalIgnoreCase));if (root == null){ Console.Error.WriteLine($"No fleet named \"{fleetName}\"."); return 1;}
// The fleet and every fleet below it.var inFleet = new HashSet<long> { root.FleetId };for (var grew = true; grew;){ grew = false; foreach (var f in fleets) if (f.ParentFleetId is { } parent && inFleet.Contains(parent) && inFleet.Add(f.FleetId)) grew = true;}
int sent = 0, failed = 0;foreach (var device in await api.ListDevicesAsync()){ if (device.FleetId is not { } fleetId || !inFleet.Contains(fleetId)) continue; try { await api.ShowMessageAsync(device.DeviceId, new ShowMessageRequest { Text = text }); sent++; } catch (OhrApiException<ProblemDetails> e) { Console.Error.WriteLine($"{device.Serial ?? device.DeviceId.ToString()}: {e.Result.Title}"); failed++; }}
Console.WriteLine($"Sent to {sent} device(s){(failed > 0 ? $", {failed} failed" : "")}.");return failed > 0 ? 1 : 0;How it works
Section titled “How it works”GET /v1/fleets gives each fleet’s parent, so the program collects the fleet and every fleet below
it, then sends POST /v1/devices/{id}/messages to each of their devices. Online devices show the
message within seconds; offline ones show it when they next check in.
Make it yours
Section titled “Make it yours”Send to devices with low battery only, to one store’s devices from your point-of-sale system, or from a button in your own admin page.