353 lines
13 KiB
C++
353 lines
13 KiB
C++
#include "device.hpp"
|
|
|
|
#include <utility>
|
|
|
|
namespace {
|
|
|
|
std::optional<std::vector<int>> asIntList(const DaliValue* value) {
|
|
if (!value) return std::nullopt;
|
|
const auto* arr = value->asArray();
|
|
if (!arr) return std::nullopt;
|
|
std::vector<int> out;
|
|
out.reserve(arr->size());
|
|
for (const auto& v : *arr) {
|
|
out.push_back(v.asInt().value_or(0));
|
|
}
|
|
return out;
|
|
}
|
|
|
|
DaliValue toIntArray(const std::optional<std::vector<int>>& values) {
|
|
if (!values.has_value()) return DaliValue();
|
|
DaliValue::Array out;
|
|
out.reserve(values->size());
|
|
for (const int v : *values) {
|
|
out.emplace_back(v);
|
|
}
|
|
return DaliValue(std::move(out));
|
|
}
|
|
|
|
} // namespace
|
|
|
|
DaliLongAddress DaliLongAddress::fromJson(const DaliValue::Object* json) {
|
|
if (!json) return DaliLongAddress{};
|
|
DaliLongAddress addr;
|
|
addr.h = getObjectInt(*json, "H").value_or(0);
|
|
addr.m = getObjectInt(*json, "M").value_or(0);
|
|
addr.l = getObjectInt(*json, "L").value_or(0);
|
|
return addr;
|
|
}
|
|
|
|
DaliValue::Object DaliLongAddress::toJson() const {
|
|
DaliValue::Object out;
|
|
out["H"] = h;
|
|
out["M"] = m;
|
|
out["L"] = l;
|
|
return out;
|
|
}
|
|
|
|
DaliDeviceCapabilities DaliDeviceCapabilities::fromJson(const DaliValue::Object* json) {
|
|
DaliDeviceCapabilities c;
|
|
if (!json) return c;
|
|
c.supportsDt1 = getObjectBool(*json, "dt1");
|
|
c.supportsDt8 = getObjectBool(*json, "dt8");
|
|
return c;
|
|
}
|
|
|
|
DaliValue::Object DaliDeviceCapabilities::toJson() const {
|
|
DaliValue::Object out;
|
|
if (supportsDt1.has_value()) out["dt1"] = supportsDt1.value();
|
|
if (supportsDt8.has_value()) out["dt8"] = supportsDt8.value();
|
|
return out;
|
|
}
|
|
|
|
void DaliDeviceCapabilities::merge(const DaliDeviceCapabilities& other) {
|
|
if (!supportsDt1.has_value()) supportsDt1 = other.supportsDt1;
|
|
if (!supportsDt8.has_value()) supportsDt8 = other.supportsDt8;
|
|
}
|
|
|
|
DaliStatusFlags DaliStatusFlags::fromJson(const DaliValue::Object* json) {
|
|
DaliStatusFlags s;
|
|
if (!json) return s;
|
|
s.controlGearPresent = getObjectBool(*json, "controlGearPresent");
|
|
s.lampFailure = getObjectBool(*json, "lampFailure");
|
|
s.lampPowerOn = getObjectBool(*json, "lampPowerOn");
|
|
s.limitError = getObjectBool(*json, "limitError");
|
|
s.fadingCompleted = getObjectBool(*json, "fadingCompleted");
|
|
s.resetState = getObjectBool(*json, "resetState");
|
|
s.missingShortAddress = getObjectBool(*json, "missingShortAddress");
|
|
s.psFault = getObjectBool(*json, "psFault");
|
|
return s;
|
|
}
|
|
|
|
DaliValue::Object DaliStatusFlags::toJson() const {
|
|
DaliValue::Object out;
|
|
if (controlGearPresent.has_value()) out["controlGearPresent"] = controlGearPresent.value();
|
|
if (lampFailure.has_value()) out["lampFailure"] = lampFailure.value();
|
|
if (lampPowerOn.has_value()) out["lampPowerOn"] = lampPowerOn.value();
|
|
if (limitError.has_value()) out["limitError"] = limitError.value();
|
|
if (fadingCompleted.has_value()) out["fadingCompleted"] = fadingCompleted.value();
|
|
if (resetState.has_value()) out["resetState"] = resetState.value();
|
|
if (missingShortAddress.has_value()) out["missingShortAddress"] = missingShortAddress.value();
|
|
if (psFault.has_value()) out["psFault"] = psFault.value();
|
|
return out;
|
|
}
|
|
|
|
bool DaliStatusFlags::hasData() const {
|
|
return controlGearPresent.has_value() || lampFailure.has_value() || lampPowerOn.has_value() ||
|
|
limitError.has_value() || fadingCompleted.has_value() || resetState.has_value() ||
|
|
missingShortAddress.has_value() || psFault.has_value();
|
|
}
|
|
|
|
void DaliStatusFlags::merge(const DaliStatusFlags& other) {
|
|
if (!controlGearPresent.has_value()) controlGearPresent = other.controlGearPresent;
|
|
if (!lampFailure.has_value()) lampFailure = other.lampFailure;
|
|
if (!lampPowerOn.has_value()) lampPowerOn = other.lampPowerOn;
|
|
if (!limitError.has_value()) limitError = other.limitError;
|
|
if (!fadingCompleted.has_value()) fadingCompleted = other.fadingCompleted;
|
|
if (!resetState.has_value()) resetState = other.resetState;
|
|
if (!missingShortAddress.has_value()) missingShortAddress = other.missingShortAddress;
|
|
if (!psFault.has_value()) psFault = other.psFault;
|
|
}
|
|
|
|
DaliDt8State DaliDt8State::fromJson(const DaliValue::Object* json) {
|
|
DaliDt8State s;
|
|
if (!json) return s;
|
|
|
|
s.colorType = getObjectInt(*json, "colorType");
|
|
s.activeMode = getObjectString(*json, "activeMode");
|
|
|
|
if (const auto* xyVal = getObjectValue(*json, "xy")) {
|
|
if (const auto* xy = xyVal->asObject()) {
|
|
s.xyX = getObjectInt(*xy, "x");
|
|
s.xyY = getObjectInt(*xy, "y");
|
|
}
|
|
}
|
|
|
|
if (const auto* gamutVal = getObjectValue(*json, "gamut")) {
|
|
if (const auto* gamut = gamutVal->asObject()) {
|
|
s.xyMinX = getObjectInt(*gamut, "xMin");
|
|
s.xyMaxX = getObjectInt(*gamut, "xMax");
|
|
s.xyMinY = getObjectInt(*gamut, "yMin");
|
|
s.xyMaxY = getObjectInt(*gamut, "yMax");
|
|
}
|
|
}
|
|
|
|
s.mirek = getObjectInt(*json, "mirek");
|
|
s.mirekMin = getObjectInt(*json, "mirekMin");
|
|
s.mirekMax = getObjectInt(*json, "mirekMax");
|
|
s.rgbwaf = asIntList(getObjectValue(*json, "rgbwaf"));
|
|
s.primaryN = asIntList(getObjectValue(*json, "primaryN"));
|
|
return s;
|
|
}
|
|
|
|
DaliValue::Object DaliDt8State::toJson() const {
|
|
DaliValue::Object out;
|
|
if (colorType.has_value()) out["colorType"] = colorType.value();
|
|
if (activeMode.has_value()) out["activeMode"] = activeMode.value();
|
|
|
|
if (xyX.has_value() || xyY.has_value()) {
|
|
DaliValue::Object xy;
|
|
if (xyX.has_value()) xy["x"] = xyX.value();
|
|
if (xyY.has_value()) xy["y"] = xyY.value();
|
|
out["xy"] = std::move(xy);
|
|
}
|
|
|
|
if (xyMinX.has_value() || xyMaxX.has_value() || xyMinY.has_value() || xyMaxY.has_value()) {
|
|
DaliValue::Object gamut;
|
|
if (xyMinX.has_value()) gamut["xMin"] = xyMinX.value();
|
|
if (xyMaxX.has_value()) gamut["xMax"] = xyMaxX.value();
|
|
if (xyMinY.has_value()) gamut["yMin"] = xyMinY.value();
|
|
if (xyMaxY.has_value()) gamut["yMax"] = xyMaxY.value();
|
|
out["gamut"] = std::move(gamut);
|
|
}
|
|
|
|
if (mirek.has_value()) out["mirek"] = mirek.value();
|
|
if (mirekMin.has_value()) out["mirekMin"] = mirekMin.value();
|
|
if (mirekMax.has_value()) out["mirekMax"] = mirekMax.value();
|
|
if (rgbwaf.has_value()) out["rgbwaf"] = toIntArray(rgbwaf);
|
|
if (primaryN.has_value()) out["primaryN"] = toIntArray(primaryN);
|
|
return out;
|
|
}
|
|
|
|
DaliDt1State DaliDt1State::fromJson(const DaliValue::Object* json) {
|
|
DaliDt1State s;
|
|
if (!json) return s;
|
|
|
|
s.emergencyLevel = getObjectInt(*json, "emergencyLevel");
|
|
s.emergencyMinLevel = getObjectInt(*json, "emergencyMinLevel");
|
|
s.emergencyMaxLevel = getObjectInt(*json, "emergencyMaxLevel");
|
|
s.prolongTimeMinutes = getObjectInt(*json, "prolongTimeMinutes");
|
|
s.ratedDurationMinutes = getObjectInt(*json, "ratedDurationMinutes");
|
|
s.testDelayTime = getObjectInt(*json, "testDelayTime");
|
|
s.failureStatus = getObjectInt(*json, "failureStatus");
|
|
s.emergencyStatus = getObjectInt(*json, "emergencyStatus");
|
|
s.emergencyMode = getObjectInt(*json, "emergencyMode");
|
|
s.feature = getObjectInt(*json, "feature");
|
|
s.version = getObjectInt(*json, "version");
|
|
return s;
|
|
}
|
|
|
|
DaliValue::Object DaliDt1State::toJson() const {
|
|
DaliValue::Object out;
|
|
if (emergencyLevel.has_value()) out["emergencyLevel"] = emergencyLevel.value();
|
|
if (emergencyMinLevel.has_value()) out["emergencyMinLevel"] = emergencyMinLevel.value();
|
|
if (emergencyMaxLevel.has_value()) out["emergencyMaxLevel"] = emergencyMaxLevel.value();
|
|
if (prolongTimeMinutes.has_value()) out["prolongTimeMinutes"] = prolongTimeMinutes.value();
|
|
if (ratedDurationMinutes.has_value()) out["ratedDurationMinutes"] = ratedDurationMinutes.value();
|
|
if (testDelayTime.has_value()) out["testDelayTime"] = testDelayTime.value();
|
|
if (failureStatus.has_value()) out["failureStatus"] = failureStatus.value();
|
|
if (emergencyStatus.has_value()) out["emergencyStatus"] = emergencyStatus.value();
|
|
if (emergencyMode.has_value()) out["emergencyMode"] = emergencyMode.value();
|
|
if (feature.has_value()) out["feature"] = feature.value();
|
|
if (version.has_value()) out["version"] = version.value();
|
|
return out;
|
|
}
|
|
|
|
DaliDevice DaliDevice::fromJson(const DaliValue::Object& json) {
|
|
DaliDevice d;
|
|
d.name = getObjectString(json, "name").value_or("");
|
|
d.id = getObjectString(json, "id").value_or("");
|
|
|
|
if (d.id.empty()) {
|
|
d.id = d.name.empty() ? "device-unknown" : d.name;
|
|
}
|
|
if (d.name.empty()) {
|
|
d.name = d.id;
|
|
}
|
|
|
|
d.shortAddress = getObjectInt(json, "shortAddress");
|
|
|
|
if (const auto* lv = getObjectValue(json, "longAddress")) {
|
|
if (const auto* lo = lv->asObject()) {
|
|
d.longAddress = DaliLongAddress::fromJson(lo);
|
|
}
|
|
}
|
|
|
|
d.isolated = getObjectBool(json, "isolated").value_or(false);
|
|
d.brightness = getObjectInt(json, "brightness");
|
|
d.groupBits = getObjectInt(json, "groupBits");
|
|
d.scenes = asIntList(getObjectValue(json, "scenes"));
|
|
|
|
d.fadeTime = getObjectInt(json, "fadeTime");
|
|
d.fadeRate = getObjectInt(json, "fadeRate");
|
|
d.powerOnLevel = getObjectInt(json, "powerOnLevel");
|
|
d.systemFailureLevel = getObjectInt(json, "systemFailureLevel");
|
|
d.minLevel = getObjectInt(json, "minLevel");
|
|
d.maxLevel = getObjectInt(json, "maxLevel");
|
|
d.operatingMode = getObjectInt(json, "operatingMode");
|
|
d.physicalMinLevel = getObjectInt(json, "physicalMinLevel");
|
|
|
|
d.deviceType = getObjectInt(json, "deviceType");
|
|
d.extType = asIntList(getObjectValue(json, "extType")).value_or(std::vector<int>{});
|
|
d.version = getObjectInt(json, "version");
|
|
|
|
if (const auto* cv = getObjectValue(json, "capabilities")) {
|
|
d.capabilities = DaliDeviceCapabilities::fromJson(cv->asObject());
|
|
}
|
|
|
|
if (const auto* dt8v = getObjectValue(json, "dt8")) {
|
|
if (const auto* obj = dt8v->asObject()) {
|
|
d.dt8 = DaliDt8State::fromJson(obj);
|
|
}
|
|
}
|
|
|
|
if (const auto* dt1v = getObjectValue(json, "dt1")) {
|
|
if (const auto* obj = dt1v->asObject()) {
|
|
d.dt1 = DaliDt1State::fromJson(obj);
|
|
}
|
|
}
|
|
|
|
if (const auto* sfv = getObjectValue(json, "statusFlags")) {
|
|
d.statusFlags = DaliStatusFlags::fromJson(sfv->asObject());
|
|
}
|
|
|
|
d.lastSyncedUtc = getObjectString(json, "lastSyncedUtc");
|
|
|
|
if (const auto* mv = getObjectValue(json, "meta")) {
|
|
if (const auto* obj = mv->asObject()) {
|
|
d.metadata = *obj;
|
|
}
|
|
}
|
|
|
|
return d;
|
|
}
|
|
|
|
DaliValue::Object DaliDevice::toJson() const {
|
|
DaliValue::Object out;
|
|
out["id"] = id;
|
|
out["name"] = name;
|
|
if (shortAddress.has_value()) out["shortAddress"] = shortAddress.value();
|
|
if (longAddress.has_value()) out["longAddress"] = longAddress->toJson();
|
|
out["isolated"] = isolated;
|
|
|
|
if (brightness.has_value()) out["brightness"] = brightness.value();
|
|
if (groupBits.has_value()) out["groupBits"] = groupBits.value();
|
|
if (scenes.has_value()) out["scenes"] = toIntArray(scenes);
|
|
|
|
if (fadeTime.has_value()) out["fadeTime"] = fadeTime.value();
|
|
if (fadeRate.has_value()) out["fadeRate"] = fadeRate.value();
|
|
if (powerOnLevel.has_value()) out["powerOnLevel"] = powerOnLevel.value();
|
|
if (systemFailureLevel.has_value()) out["systemFailureLevel"] = systemFailureLevel.value();
|
|
if (minLevel.has_value()) out["minLevel"] = minLevel.value();
|
|
if (maxLevel.has_value()) out["maxLevel"] = maxLevel.value();
|
|
if (operatingMode.has_value()) out["operatingMode"] = operatingMode.value();
|
|
if (physicalMinLevel.has_value()) out["physicalMinLevel"] = physicalMinLevel.value();
|
|
|
|
if (deviceType.has_value()) out["deviceType"] = deviceType.value();
|
|
if (!extType.empty()) {
|
|
DaliValue::Array arr;
|
|
arr.reserve(extType.size());
|
|
for (const int t : extType) arr.emplace_back(t);
|
|
out["extType"] = std::move(arr);
|
|
}
|
|
if (version.has_value()) out["version"] = version.value();
|
|
|
|
const auto caps = capabilities.toJson();
|
|
if (!caps.empty()) out["capabilities"] = caps;
|
|
|
|
if (dt8.has_value()) out["dt8"] = dt8->toJson();
|
|
if (dt1.has_value()) out["dt1"] = dt1->toJson();
|
|
|
|
if (statusFlags.hasData()) out["statusFlags"] = statusFlags.toJson();
|
|
if (lastSyncedUtc.has_value()) out["lastSyncedUtc"] = lastSyncedUtc.value();
|
|
if (!metadata.empty()) out["meta"] = metadata;
|
|
|
|
return out;
|
|
}
|
|
|
|
std::string DaliDevice::displayName() const { return name.empty() ? id : name; }
|
|
|
|
void DaliDevice::merge(const DaliDevice& other) {
|
|
if (other.shortAddress.has_value()) shortAddress = other.shortAddress;
|
|
if (!longAddress.has_value()) longAddress = other.longAddress;
|
|
isolated = other.isolated;
|
|
|
|
if (!brightness.has_value()) brightness = other.brightness;
|
|
if (!groupBits.has_value()) groupBits = other.groupBits;
|
|
if (!scenes.has_value() && other.scenes.has_value()) scenes = other.scenes;
|
|
|
|
if (!fadeTime.has_value()) fadeTime = other.fadeTime;
|
|
if (!fadeRate.has_value()) fadeRate = other.fadeRate;
|
|
if (!powerOnLevel.has_value()) powerOnLevel = other.powerOnLevel;
|
|
if (!systemFailureLevel.has_value()) systemFailureLevel = other.systemFailureLevel;
|
|
if (!minLevel.has_value()) minLevel = other.minLevel;
|
|
if (!maxLevel.has_value()) maxLevel = other.maxLevel;
|
|
if (!operatingMode.has_value()) operatingMode = other.operatingMode;
|
|
if (!physicalMinLevel.has_value()) physicalMinLevel = other.physicalMinLevel;
|
|
|
|
if (!deviceType.has_value()) deviceType = other.deviceType;
|
|
if (extType.empty() && !other.extType.empty()) extType = other.extType;
|
|
if (!version.has_value()) version = other.version;
|
|
|
|
capabilities.merge(other.capabilities);
|
|
if (!dt8.has_value() && other.dt8.has_value()) dt8 = other.dt8;
|
|
if (!dt1.has_value() && other.dt1.has_value()) dt1 = other.dt1;
|
|
|
|
statusFlags.merge(other.statusFlags);
|
|
if (other.lastSyncedUtc.has_value()) lastSyncedUtc = other.lastSyncedUtc;
|
|
|
|
for (const auto& kv : other.metadata) {
|
|
metadata[kv.first] = kv.second;
|
|
}
|
|
}
|