using System; | |
using System.IO; | |
using System.Collections.Generic; | |
using System.Threading.Tasks; | |
using Microsoft.AspNetCore.Mvc; | |
using Microsoft.Azure.WebJobs; | |
using Microsoft.Azure.WebJobs.Extensions.Http; | |
using Microsoft.AspNetCore.Http; | |
using Microsoft.Extensions.Logging; | |
using Newtonsoft.Json; | |
using System.Net.Http; | |
using System.Net.Http.Headers; | |
using System.Linq; | |
namespace dk.ostebaronen | |
{ | |
public static class DevOpsStatus | |
{ | |
private const string Pat = "your-azure-devops-pat"; | |
private static HttpClient _httpClient; | |
[FunctionName("DevOpsStatus")] | |
public static async Task<IActionResult> Run( | |
[HttpTrigger(AuthorizationLevel.Function, "get", Route = null)] HttpRequest req, | |
ILogger log) | |
{ | |
log.LogInformation("C# HTTP trigger function processed a request."); | |
var lastBuild = await GetLatestBuild().ConfigureAwait(false); | |
if (lastBuild == null) | |
{ | |
return new NotFoundObjectResult("No build found"); | |
} | |
foreach(var build in lastBuild) | |
{ | |
log.LogInformation($"{build.BuildNumber}: {build.Status} - {build.Result} - {build.Url}"); | |
} | |
var anyFailed = lastBuild.Any(b => b.Status?.ToLowerInvariant() == "failed"); | |
var anyRunning = lastBuild.Any(b => b.Result.ToLowerInvariant() == "inprogress"); | |
var allFinished = lastBuild.All(b => b.Result.ToLowerInvariant() == "succeeded" && b.Status?.ToLowerInvariant() == "completed"); | |
if (anyFailed) | |
return new OkObjectResult("failed"); | |
if (anyRunning) | |
return new OkObjectResult("running"); | |
if (allFinished) | |
return new OkObjectResult("finished"); | |
return new OkObjectResult("unknown"); | |
} | |
private static HttpClient EnsureHttpClient() | |
{ | |
if (_httpClient == null) | |
{ | |
_httpClient = new HttpClient(); | |
_httpClient.DefaultRequestHeaders.Authorization | |
= new AuthenticationHeaderValue( | |
"Basic", Pat); | |
_httpClient.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("Cheesebulb", "1.0")); | |
} | |
return _httpClient; | |
} | |
private static async Task<IEnumerable<Build>> GetLatestBuild(int top = 4) | |
{ | |
var client = EnsureHttpClient(); | |
var organization = "your-azure-devops-org"; | |
var project = "your-project-id"; | |
var url = $"https://dev.azure.com/{organization}/{project}/_apis/build/builds?$top={top}&api-version=5.0"; | |
var response = await client.GetAsync(url).ConfigureAwait(false); | |
using (response) | |
{ | |
if (response.IsSuccessStatusCode) | |
{ | |
var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); | |
using (responseStream) | |
using (var streamReader = new StreamReader(responseStream)) | |
using (var jsonReader = new JsonTextReader(streamReader)) | |
{ | |
var serializer = new JsonSerializer(); | |
var results = serializer.Deserialize<Welcome>(jsonReader); | |
return results.Builds.Take(4); | |
} | |
} | |
} | |
return null; | |
} | |
} | |
public class Welcome | |
{ | |
[JsonProperty("count")] | |
public long Count { get; set; } | |
[JsonProperty("value")] | |
public Build[] Builds { get; set; } | |
} | |
public class Build | |
{ | |
[JsonProperty("id")] | |
public long Id { get; set; } | |
[JsonProperty("buildNumber")] | |
public string BuildNumber { get; set; } | |
[JsonProperty("status")] | |
public string Status { get; set; } | |
[JsonProperty("result")] | |
public string Result { get; set; } | |
[JsonProperty("queueTime")] | |
public DateTimeOffset QueueTime { get; set; } | |
[JsonProperty("startTime")] | |
public DateTimeOffset StartTime { get; set; } | |
[JsonProperty("finishTime")] | |
public DateTimeOffset FinishTime { get; set; } | |
[JsonProperty("url")] | |
public Uri Url { get; set; } | |
} | |
} |
#include <Arduino.h> | |
#include <ESP8266WiFi.h> | |
#include <WiFiClientSecure.h> | |
#include <NeoPixelBus.h> | |
const char* ssid = "your-wifi-ssid"; | |
const char* password = "hunter42"; | |
const unsigned long HTTP_TIMEOUT = 10000; | |
const char* host = "yourfunction.azurewebsites.net"; | |
const char* request = "/api/YourFunctionName?code=code"; | |
const int httpsPort = 443; | |
const uint8_t PixelCount = 9; | |
const uint8_t PixelPin = 0; // doesn't matter what you put here | |
NeoPixelBus<NeoGrbFeature, NeoEsp8266Dma800KbpsMethod> strip(PixelCount, PixelPin); | |
RgbColor off(0, 0, 0); | |
RgbColor red(255, 0, 0); | |
RgbColor green(0, 255, 0); | |
RgbColor blue(0, 0, 255); | |
RgbColor yellow(255, 255, 0); | |
RgbColor teal(0, 255, 255); | |
RgbColor purple(255, 0, 255); | |
RgbColor orange(255, 153, 0); | |
void setAllLedsColor(RgbColor color) | |
{ | |
for(uint8_t i = 0; i < PixelCount; i++) { | |
strip.SetPixelColor(i, color); | |
} | |
strip.Show(); | |
} | |
bool initializeWifi() { | |
if (WiFi.status() == WL_CONNECTED) | |
{ | |
Serial.println("WiFi already connected"); | |
return true; | |
} | |
Serial.print("Connecting to "); | |
Serial.println(ssid); | |
WiFi.mode(WIFI_STA); | |
WiFi.begin(ssid, password); | |
for(uint8_t i = 0; i < 100; i++) { | |
if (WiFi.status() != WL_CONNECTED) { | |
// blink the leds while connecting to WiFi | |
setAllLedsColor(off); | |
delay(400); | |
setAllLedsColor(purple); | |
Serial.print("."); | |
delay(400); | |
} | |
} | |
if (WiFi.status() == WL_CONNECTED) { | |
setAllLedsColor(orange); | |
Serial.println(""); | |
Serial.println("WiFi connected"); | |
Serial.println("IP address "); | |
Serial.println(WiFi.localIP()); | |
return true; | |
} | |
setAllLedsColor(off); | |
WiFi.disconnect(); | |
return false; | |
} | |
void setup() { | |
Serial.begin(115200); | |
strip.Begin(); | |
strip.Show(); | |
setAllLedsColor(off); | |
initializeWifi(); | |
} | |
WiFiClientSecure client; | |
bool connect() { | |
Serial.print("connecting to "); | |
Serial.println(host); | |
bool ok = client.connect(host, httpsPort); | |
Serial.println(ok ? "Connected Http" : "Connection Http Failed!"); | |
return ok; | |
} | |
void disconnect() { | |
Serial.println("Disconnect Http"); | |
client.stop(); | |
} | |
void sendRequest() { | |
client.print("GET "); | |
client.print(request); | |
client.println(" HTTP/1.1"); | |
client.print("Host: "); | |
client.println(host); | |
client.println("User-Agent: Cheesebulb/1.0"); | |
client.println("Connection: close"); | |
client.println(); | |
} | |
void skipHeaders() { | |
while (client.connected()) { | |
String line = client.readStringUntil('\n'); | |
if (line == "\r") { | |
Serial.println("headers received"); | |
break; | |
} | |
} | |
} | |
String lastStatus; | |
void readResponseContent() { | |
String status = client.readStringUntil('\n'); | |
Serial.println(status); | |
if (status == lastStatus) { | |
// no need to do anything | |
return; | |
} | |
if (status == "failed") { | |
setAllLedsColor(red); | |
} | |
else if (status == "running") { | |
setAllLedsColor(blue); | |
} | |
else if (status == "finished") { | |
setAllLedsColor(green); | |
} | |
else { | |
setAllLedsColor(yellow); | |
} | |
} | |
void getBuildStatus() | |
{ | |
if (connect()) { | |
sendRequest(); | |
skipHeaders(); | |
readResponseContent(); | |
} | |
disconnect(); | |
} | |
uint8_t reconnects = 0; | |
void loop() { | |
if (initializeWifi()) { | |
getBuildStatus(); | |
reconnects = 0; | |
delay(60000); | |
} | |
else { | |
lastStatus = "derp"; | |
reconnects = reconnects + 1; | |
if (reconnects == 120) { // 10 minutes | |
ESP.reset(); | |
} | |
// delay a bit and retry wifi | |
delay(5000); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment