Skip to content

Commit

Permalink
Add a RachioController device type.
Browse files Browse the repository at this point in the history
  • Loading branch information
aholmes committed Oct 21, 2023
1 parent 7de6f4c commit 851e349
Show file tree
Hide file tree
Showing 4 changed files with 330 additions and 1 deletion.
3 changes: 3 additions & 0 deletions hubitat2prom/HubitatDevice/DeviceTypes/DeviceType.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using AttributeValue = OneOf.OneOf<string, string[], int?, double?, OneOf.OneOf<double, string>?>;
using hubitat2prom.HubitatDevice;
using Rachio = hubitat2prom.HubitatDevice.DeviceTypes.Rachio;

namespace hubitat2prom.PrometheusExporter.DeviceTypes;

Expand All @@ -16,6 +17,8 @@ public static DeviceType CreateDeviceType(DeviceSummary deviceSummary)
{
case "Flume Device":
return new FlumeDevice();
case "Rachio Controller":
return new Rachio.RachioControllerDevice();
default:
return new GenericDevice();
}
Expand Down
61 changes: 61 additions & 0 deletions hubitat2prom/HubitatDevice/DeviceTypes/FlumeDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,67 @@
namespace hubitat2prom.PrometheusExporter.DeviceTypes;


/// <summary>
/// Flume Device. The JSON response is structured like:
/// {
/// "name": "Flume",
/// "label": "Flume",
/// "type": "Flume Device",
/// "id": "1319",
/// "date": "2023-10-21T20:09:39+0000",
/// "model": null,
/// "manufacturer": null,
/// "room": "Outside",
/// "capabilities": [
/// "WaterSensor",
/// "Configuration",
/// "Refresh",
/// "Initialize",
/// "PresenceSensor"
/// ],
/// "attributes": {
/// "usageLastDay": "111.02",
/// "dataType": "ENUM",
/// "values": [
/// "present",
/// "not present"
/// ],
/// "usageLastHour": "0.0",
/// "notificationStream": "[2023-09-30T01:55:00.000Z]: High Flow Alert triggered at 4243 Coolidge. Water has been running for 15 minutes averaging 9.06 gallons every minute.",
/// "water": "dry",
/// "usageLastMonth": "2594.13",
/// "usageLastWeek": "548.59",
/// "flowStatus": "stopped",
/// "commStatus": "good",
/// "flowDurationMin": "0",
/// "usageLastMinute": "0.0",
/// "presence": "present"
/// },
/// "commands": [
/// {
/// "command": "clearAwayMode"
/// },
/// {
/// "command": "clearWetStatus"
/// },
/// {
/// "command": "configure"
/// },
/// {
/// "command": "initialize"
/// },
/// {
/// "command": "refresh"
/// },
/// {
/// "command": "setAwayMode"
/// },
/// {
/// "command": "testWetStatus"
/// }
/// ]
/// },
/// </summary>
public class FlumeDevice: GenericDevice
{
public FlumeDevice() { }
Expand Down
29 changes: 28 additions & 1 deletion hubitat2prom/HubitatDevice/DeviceTypes/GenericDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,19 @@ public override double ExtractMetric(string attributeName, AttributeValue attrib
@string =>
{
var name = attributeName.ToLowerInvariant();
// TODO consider removing name=="switch" and
// instead put TryGetBoolean first.
if (name == "switch") return @string == "on" ? 1 : 0;
if (name == "power" && TryGetPower(@string, out value)) return value;
if (name == "thermostatoperatingstate" && TryGetThermostatOperatingState(@string, out value)) return value;
if (name == "thermostatmode" && TryGetThermostatMode(@string, out value)) return value;
if (name == "contact") return @string == "closed" ? 1 : 0;
// many devices use on/off/true/false for different
// attributes. try to capture that here before
// bailing out with the default value.
if (TryGetBoolean(@string, out value)) return value;
return MISSING_VALUE_DEFAULT;
},
stringArray => MISSING_VALUE_DEFAULT,
Expand All @@ -35,6 +42,26 @@ public override double ExtractMetric(string attributeName, AttributeValue attrib
return metricValue;
}

private static bool TryGetBoolean(string stringValue, out double value)
{
stringValue = stringValue.ToLowerInvariant();

if (stringValue == "off" || stringValue == "false")
{
value = 0;
return true;
}

if (stringValue == "on" || stringValue == "true")
{
value = 1;
return true;
}

value = -1;
return false;
}

private static bool TryGetPower(string power, out double value)
{
if (power == "off")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
using hubitat2prom.PrometheusExporter.DeviceTypes;
using AttributeValue = OneOf.OneOf<string, string[], int?, double?, OneOf.OneOf<double, string>?>;

namespace hubitat2prom.HubitatDevice.DeviceTypes.Rachio;


/// <summary>
/// Rachio Controller Device. The JSON response is structured like:
/// {
/// "name": "Rachio Controller",
/// "label": "Rachio - Rachio",
/// "type": "Rachio Controller",
/// "id": "1389",
/// "date": "2023-10-21T20:20:16+0000",
/// "model": null,
/// "manufacturer": null,
/// "room": null,
/// "capabilities": [
/// "Actuator",
/// "Refresh",
/// "Polling",
/// "WaterSensor",
/// "Valve",
/// "Switch",
/// "Sensor",
/// "HealthCheck"
/// ],
/// "attributes": {
/// "rainSensorTripped": "false",
/// "dataType": "STRING",
/// "values": null,
/// "activeZoneCnt": "3",
/// "checkInterval": null,
/// "nextEventTime": "Oct 26, 2023 - 6:40:07 AM",
/// "controllerOn": "true",
/// "curZoneName": "...",
/// "curZoneRunStatus": "Status: Idle",
/// "notificationMessage": null,
/// "nextEvent": "..."
/// "water": "dry",
/// "hardwareDesc": "8-Zone (Gen 3)",
/// "nextRunStr": "Oct 26, 2023 - 6:40:07 AM",
/// "nextEventWeatherIntelligence": null,
/// "lastRun": "1697488267000",
/// "valve": "close",
/// "watering": "off",
/// "curZoneNumber": "3",
/// "lastUpdatedDt": "Oct 21, 2023 - 1:20:16 PM",
/// "rainDelay": "0",
/// "nextRun": "1698352807000",
/// "curZoneStartDate": "Not Active",
/// "rainDelayStr": "No Rain Delay",
/// "curZoneDuration": "1673",
/// "monthlyWeatherIntelligenceCount": "0",
/// "curZoneIsCycling": "False",
/// "DeviceWatch-DeviceStatus": "online",
/// "scheduleType": "Off",
/// "switch": "off",
/// "monthlyMinutesSaved": "0.0",
/// "standbyMode": "off",
/// "curZoneCycleCount": "0",
/// "nextEventThresholdInfo": null,
/// "nextEventType": "FUTURE_FEED_SCHEDULE_EVENT",
/// "monthlyMinutesUsed": "56.0",
/// "dashboard": "...",
/// "curZoneWaterTime": "10",
/// "hardwareModel": "8ZoneV3",
/// "lastRunStr": "Oct 16, 2023 - 6:31:07 AM"
/// },
/// "commands": [
/// {
/// "command": "close"
/// },
/// {
/// "command": "decZoneWaterTime"
/// },
/// {
/// "command": "decreaseRainDelay"
/// },
/// {
/// "command": "doSetRainDelay"
/// },
/// {
/// "command": "incZoneWaterTime"
/// },
/// {
/// "command": "increaseRainDelay"
/// },
/// {
/// "command": "off"
/// },
/// {
/// "command": "on"
/// },
/// {
/// "command": "open"
/// },
/// {
/// "command": "pauseZoneRun"
/// },
/// {
/// "command": "ping"
/// },
/// {
/// "command": "poll"
/// },
/// {
/// "command": "refresh"
/// },
/// {
/// "command": "resumeZoneRun"
/// },
/// {
/// "command": "runAllZones"
/// },
/// {
/// "command": "setDashboardColorScheme"
/// },
/// {
/// "command": "setDashboardIconScheme"
/// },
/// {
/// "command": "setRainDelay"
/// },
/// {
/// "command": "setZoneWaterTime"
/// },
/// {
/// "command": "standbyOff"
/// },
/// {
/// "command": "standbyOn"
/// },
/// {
/// "command": "stopWatering"
/// }
/// ]
/// },
/// </summary>
public class RachioControllerDevice : GenericDevice
{
public RachioControllerDevice() { }

public override double ExtractMetric(string attributeName, AttributeValue attributeValue)
{
// We can return if the value type is not T0 (string)
// because that is the only type this method extracts
if (!attributeValue.IsT0) return base.ExtractMetric(attributeName, attributeValue);
var attributeStringValue = attributeValue.AsT0;
double value;
// TODO Rachio has a lot of date-type fields
// figure out their formats and add them as timestamps
switch (attributeName)
{
case "curzonerunstatus":
if (TryGetCurrentZoneRunStatus(attributeStringValue, out value)) return value;
break;
case "water":
if (TryGetWater(attributeStringValue, out value)) return value;
break;
case "valve":
if (TryGetValve(attributeStringValue, out value)) return value;
break;
case "devicewatch-devicestatus":
if (TryGetDeviceWatchDeviceStatus(attributeStringValue, out value)) return value;
break;
case "nexteventtype":
if (TryGetNextEventType(attributeStringValue, out value)) return value;
break;
default:
System.Diagnostics.Debug.WriteLine($"Unknown attribute \"{attributeName}\" from Flume device.");
break;
}

return MISSING_VALUE_DEFAULT;
}

private static bool TryGetCurrentZoneRunStatus(string zoneRunStatus, out double value)
{
switch (zoneRunStatus)
{
case "Status: Idle": value = 0; return true;
// TODO are these correct?
case "Device is Offline": value = 1; return true;
case "Device in Standby Mode": value = 2; return true;
}

value = -1;
return false;
}

private static bool TryGetValve(string valve, out double value)
{
switch (valve)
{
case "close": value = 0; return true;
case "open": value = 1; return true;
}

value = -1;
return false;
}

private static bool TryGetDeviceWatchDeviceStatus(string deviceWatchDeviceStatus, out double value)
{
switch (deviceWatchDeviceStatus)
{
case "offline": value = 0; return true;
case "online": value = 1; return true;
}

value = -1;
return false;
}

private static bool TryGetWater(string water, out double value)
{
switch (water)
{
case "dry": value = 0; return true;
case "wet": value = 1; return true;
}

value = -1;
return false;
}

private static bool TryGetNextEventType(string water, out double value)
{
switch (water)
{
case "FUTURE_FEED_SCHEDULE_EVENT": value = 0; return true;
}

value = -1;
return false;
}
}

0 comments on commit 851e349

Please sign in to comment.