in HAP/HAPIPAccessoryProtocol.c [545:1055]
HAPError HAPIPAccessoryProtocolGetCharacteristicReadResponseBytes(
HAPAccessoryServerRef* server,
HAPIPReadContextRef* readContexts,
size_t numReadContexts,
HAPIPReadRequestParameters* parameters,
HAPIPByteBuffer* buffer) {
HAPPrecondition(server);
HAPPrecondition(parameters);
HAPPrecondition(buffer);
HAPError err;
int n, success;
size_t i;
char scratch_string[64];
i = 0;
HAPAssert(i <= numReadContexts);
while ((i < numReadContexts) && (((HAPIPReadContext*) &readContexts[i])->status == 0)) {
i++;
}
HAPIPReadContext* readContext = (HAPIPReadContext*) &readContexts[i];
HAPAssert((i == numReadContexts) || ((i < numReadContexts) && (readContext->status != 0)));
success = i == numReadContexts;
err = HAPIPByteBufferAppendStringWithFormat(buffer, "{\"characteristics\":[");
if (err) {
goto error;
}
for (i = 0; i < numReadContexts; i++) {
readContext = (HAPIPReadContext*) &readContexts[i];
const HAPBaseCharacteristic* chr_ = GetCharacteristic(server, readContext->aid, readContext->iid);
HAPAssert(chr_ || (readContext->status != 0));
err = HAPIPByteBufferAppendStringWithFormat(buffer, "%s{\"aid\":", i == 0 ? "" : ",");
if (err) {
goto error;
}
err = HAPUInt64GetDescription(uintval(readContext->aid), scratch_string, sizeof scratch_string);
HAPAssert(!err);
err = HAPIPByteBufferAppendStringWithFormat(buffer, "%s", scratch_string);
if (err) {
goto error;
}
err = HAPIPByteBufferAppendStringWithFormat(buffer, ",\"iid\":");
if (err) {
goto error;
}
err = HAPUInt64GetDescription(uintval(readContext->iid), scratch_string, sizeof scratch_string);
HAPAssert(!err);
err = HAPIPByteBufferAppendStringWithFormat(buffer, "%s", scratch_string);
if (err) {
goto error;
}
if (parameters->type && chr_) {
err = HAPUUIDGetDescription(chr_->characteristicType, scratch_string, sizeof scratch_string);
HAPAssert(!err);
err = HAPIPByteBufferAppendStringWithFormat(buffer, ",\"type\":\"%s\"", scratch_string);
if (err) {
goto error;
}
}
if (parameters->meta && chr_) {
switch (chr_->format) {
case kHAPCharacteristicFormat_Bool: {
err = HAPIPByteBufferAppendStringWithFormat(buffer, ",\"format\":\"bool\"");
} break;
case kHAPCharacteristicFormat_UInt8: {
err = HAPIPByteBufferAppendStringWithFormat(buffer, ",\"format\":\"uint8\"");
} break;
case kHAPCharacteristicFormat_UInt16: {
err = HAPIPByteBufferAppendStringWithFormat(buffer, ",\"format\":\"uint16\"");
} break;
case kHAPCharacteristicFormat_UInt32: {
err = HAPIPByteBufferAppendStringWithFormat(buffer, ",\"format\":\"uint32\"");
} break;
case kHAPCharacteristicFormat_UInt64: {
err = HAPIPByteBufferAppendStringWithFormat(buffer, ",\"format\":\"uint64\"");
} break;
case kHAPCharacteristicFormat_Int: {
err = HAPIPByteBufferAppendStringWithFormat(buffer, ",\"format\":\"int\"");
} break;
case kHAPCharacteristicFormat_Float: {
err = HAPIPByteBufferAppendStringWithFormat(buffer, ",\"format\":\"float\"");
} break;
case kHAPCharacteristicFormat_String: {
err = HAPIPByteBufferAppendStringWithFormat(buffer, ",\"format\":\"string\"");
} break;
case kHAPCharacteristicFormat_TLV8: {
err = HAPIPByteBufferAppendStringWithFormat(buffer, ",\"format\":\"tlv8\"");
} break;
case kHAPCharacteristicFormat_Data: {
err = HAPIPByteBufferAppendStringWithFormat(buffer, ",\"format\":\"data\"");
} break;
}
if (err) {
goto error;
}
}
if (readContext->status == 0) {
HAPAssert(chr_);
if (!success) {
err = HAPIPByteBufferAppendStringWithFormat(buffer, ",\"status\":0");
if (err) {
goto error;
}
}
if (HAPUUIDAreEqual(chr_->characteristicType, &kHAPCharacteristicType_ProgrammableSwitchEvent)) {
// A read of this characteristic must always return a null value for IP accessories.
// See HomeKit Accessory Protocol Specification R14
// Section 9.75 Programmable Switch Event
const HAPAccessory* accessory = HAPNonnull(GetAccessory(server, readContext->aid));
HAPLogCharacteristicInfo(
&logObject,
chr_,
service,
accessory,
"Sending null value (readHandler callback is only called for HAP events).");
err = HAPIPByteBufferAppendStringWithFormat(buffer, ",\"value\":null}");
} else {
switch (chr_->format) {
case kHAPCharacteristicFormat_Bool: {
err = HAPIPByteBufferAppendStringWithFormat(
buffer, ",\"value\":%s}", readContext->value.unsignedIntValue ? "1" : "0");
} break;
case kHAPCharacteristicFormat_UInt8:
case kHAPCharacteristicFormat_UInt16:
case kHAPCharacteristicFormat_UInt32:
case kHAPCharacteristicFormat_UInt64: {
err = HAPIPByteBufferAppendStringWithFormat(buffer, ",\"value\":");
if (err) {
goto error;
}
err = HAPUInt64GetDescription(
uintval(readContext->value.unsignedIntValue), scratch_string, sizeof scratch_string);
HAPAssert(!err);
err = HAPIPByteBufferAppendStringWithFormat(buffer, "%s", scratch_string);
if (err) {
goto error;
}
err = HAPIPByteBufferAppendStringWithFormat(buffer, "}");
} break;
case kHAPCharacteristicFormat_Int: {
err = HAPIPByteBufferAppendStringWithFormat(
buffer, ",\"value\":%ld}", (long) readContext->value.intValue);
} break;
case kHAPCharacteristicFormat_Float: {
err = HAPJSONUtilsGetFloatDescription(
readContext->value.floatValue, scratch_string, sizeof scratch_string);
HAPAssert(!err);
err = HAPIPByteBufferAppendStringWithFormat(buffer, ",\"value\":%s}", scratch_string);
} break;
case kHAPCharacteristicFormat_String:
case kHAPCharacteristicFormat_TLV8:
case kHAPCharacteristicFormat_Data: {
err = HAPIPByteBufferAppendStringWithFormat(buffer, ",\"value\":\"");
if (err) {
goto error;
}
size_t bufferMark = buffer->position;
err = HAPIPByteBufferAppendStringWithFormat(buffer, "%s", readContext->value.stringValue.bytes);
if (err) {
goto error;
}
size_t numStringDataBytes = readContext->value.stringValue.numBytes;
err = HAPJSONUtilsEscapeStringData(
&buffer->data[bufferMark], buffer->limit - bufferMark, &numStringDataBytes);
if (err) {
goto error;
}
buffer->position = bufferMark + numStringDataBytes;
err = HAPIPByteBufferAppendStringWithFormat(buffer, "\"}");
} break;
}
}
if (err) {
goto error;
}
} else {
err = HAPIPByteBufferAppendStringWithFormat(buffer, ",\"status\":%ld}", (long) readContext->status);
if (err) {
goto error;
}
}
if (parameters->perms && chr_) {
// See HomeKit Accessory Protocol Specification R14
// Section 6.3.3 Characteristic Objects
err = HAPIPByteBufferAppendStringWithFormat(buffer, ",\"perms\":[");
if (err) {
goto error;
}
n = 0;
if (chr_->properties.readable) {
err = HAPIPByteBufferAppendStringWithFormat(buffer, "%s\"pr\"", n == 0 ? "" : ",");
if (err) {
goto error;
}
n++;
}
if (chr_->properties.writable) {
err = HAPIPByteBufferAppendStringWithFormat(buffer, "%s\"pw\"", n == 0 ? "" : ",");
if (err) {
goto error;
}
n++;
}
if (chr_->properties.supportsEventNotification) {
err = HAPIPByteBufferAppendStringWithFormat(buffer, "%s\"ev\"", n == 0 ? "" : ",");
if (err) {
goto error;
}
n++;
}
if (chr_->properties.supportsAuthorizationData) {
err = HAPIPByteBufferAppendStringWithFormat(buffer, "%s\"aa\"", n == 0 ? "" : ",");
if (err) {
goto error;
}
n++;
}
if (chr_->properties.requiresTimedWrite) {
err = HAPIPByteBufferAppendStringWithFormat(buffer, "%s\"tw\"", n == 0 ? "" : ",");
if (err) {
goto error;
}
n++;
}
if (chr_->properties.ip.supportsWriteResponse) {
err = HAPIPByteBufferAppendStringWithFormat(buffer, "%s\"wr\"", n == 0 ? "" : ",");
if (err) {
goto error;
}
n++;
}
if (chr_->properties.hidden) {
err = HAPIPByteBufferAppendStringWithFormat(buffer, "%s\"hd\"", n == 0 ? "" : ",");
if (err) {
goto error;
}
n++;
}
err = HAPIPByteBufferAppendStringWithFormat(buffer, "]");
if (err) {
goto error;
}
}
if (parameters->ev && chr_) {
err = HAPIPByteBufferAppendStringWithFormat(buffer, ",\"ev\":%s", readContext->ev ? "true" : "false");
if (err) {
goto error;
}
}
if (parameters->meta && chr_) {
HAPCharacteristicUnits unit = kHAPCharacteristicUnits_None;
switch (chr_->format) {
case kHAPCharacteristicFormat_Bool: {
} break;
case kHAPCharacteristicFormat_UInt8: {
unit = ((const HAPUInt8Characteristic*) chr_)->units;
} break;
case kHAPCharacteristicFormat_UInt16: {
unit = ((const HAPUInt16Characteristic*) chr_)->units;
} break;
case kHAPCharacteristicFormat_UInt32: {
unit = ((const HAPUInt32Characteristic*) chr_)->units;
} break;
case kHAPCharacteristicFormat_UInt64: {
unit = ((const HAPUInt64Characteristic*) chr_)->units;
} break;
case kHAPCharacteristicFormat_Int: {
unit = ((const HAPIntCharacteristic*) chr_)->units;
} break;
case kHAPCharacteristicFormat_Float: {
unit = ((const HAPFloatCharacteristic*) chr_)->units;
} break;
case kHAPCharacteristicFormat_String:
case kHAPCharacteristicFormat_TLV8:
case kHAPCharacteristicFormat_Data: {
} break;
}
switch (unit) {
case kHAPCharacteristicUnits_None: {
} break;
case kHAPCharacteristicUnits_Celsius: {
err = HAPIPByteBufferAppendStringWithFormat(buffer, ",\"unit\":\"celsius\"");
} break;
case kHAPCharacteristicUnits_ArcDegrees: {
err = HAPIPByteBufferAppendStringWithFormat(buffer, ",\"unit\":\"arcdegrees\"");
} break;
case kHAPCharacteristicUnits_Percentage: {
err = HAPIPByteBufferAppendStringWithFormat(buffer, ",\"unit\":\"percentage\"");
} break;
case kHAPCharacteristicUnits_Lux: {
err = HAPIPByteBufferAppendStringWithFormat(buffer, ",\"unit\":\"lux\"");
} break;
case kHAPCharacteristicUnits_Seconds: {
err = HAPIPByteBufferAppendStringWithFormat(buffer, ",\"unit\":\"seconds\"");
} break;
}
if (err) {
goto error;
}
switch (chr_->format) {
case kHAPCharacteristicFormat_Bool: {
} break;
case kHAPCharacteristicFormat_UInt8: {
const HAPUInt8Characteristic* chr = (const HAPUInt8Characteristic*) chr_;
uint8_t minimumValue = chr->constraints.minimumValue;
uint8_t maximumValue = chr->constraints.maximumValue;
uint8_t stepValue = chr->constraints.stepValue;
HAPAssert(minimumValue <= maximumValue);
if (minimumValue || maximumValue != UINT8_MAX) {
err = HAPUInt64GetDescription(uintval(minimumValue), scratch_string, sizeof scratch_string);
HAPAssert(!err);
err = HAPIPByteBufferAppendStringWithFormat(buffer, ",\"minValue\":%s", scratch_string);
if (err) {
goto error;
}
err = HAPUInt64GetDescription(uintval(maximumValue), scratch_string, sizeof scratch_string);
HAPAssert(!err);
err = HAPIPByteBufferAppendStringWithFormat(buffer, ",\"maxValue\":%s", scratch_string);
if (err) {
goto error;
}
err = HAPUInt64GetDescription(uintval(stepValue), scratch_string, sizeof scratch_string);
HAPAssert(!err);
err = HAPIPByteBufferAppendStringWithFormat(buffer, ",\"minStep\":%s", scratch_string);
if (err) {
goto error;
}
}
} break;
case kHAPCharacteristicFormat_UInt16: {
const HAPUInt16Characteristic* chr = (const HAPUInt16Characteristic*) chr_;
uint16_t minimumValue = chr->constraints.minimumValue;
uint16_t maximumValue = chr->constraints.maximumValue;
uint16_t stepValue = chr->constraints.stepValue;
HAPAssert(minimumValue <= maximumValue);
if (minimumValue || maximumValue != UINT16_MAX) {
err = HAPUInt64GetDescription(uintval(minimumValue), scratch_string, sizeof scratch_string);
HAPAssert(!err);
err = HAPIPByteBufferAppendStringWithFormat(buffer, ",\"minValue\":%s", scratch_string);
if (err) {
goto error;
}
err = HAPUInt64GetDescription(uintval(maximumValue), scratch_string, sizeof scratch_string);
HAPAssert(!err);
err = HAPIPByteBufferAppendStringWithFormat(buffer, ",\"maxValue\":%s", scratch_string);
if (err) {
goto error;
}
err = HAPUInt64GetDescription(uintval(stepValue), scratch_string, sizeof scratch_string);
HAPAssert(!err);
err = HAPIPByteBufferAppendStringWithFormat(buffer, ",\"minStep\":%s", scratch_string);
if (err) {
goto error;
}
}
} break;
case kHAPCharacteristicFormat_UInt32: {
const HAPUInt32Characteristic* chr = (const HAPUInt32Characteristic*) chr_;
uint32_t minimumValue = chr->constraints.minimumValue;
uint32_t maximumValue = chr->constraints.maximumValue;
uint32_t stepValue = chr->constraints.stepValue;
HAPAssert(minimumValue <= maximumValue);
if (minimumValue || maximumValue != UINT32_MAX) {
err = HAPUInt64GetDescription(uintval(minimumValue), scratch_string, sizeof scratch_string);
HAPAssert(!err);
err = HAPIPByteBufferAppendStringWithFormat(buffer, ",\"minValue\":%s", scratch_string);
if (err) {
goto error;
}
err = HAPUInt64GetDescription(uintval(maximumValue), scratch_string, sizeof scratch_string);
HAPAssert(!err);
err = HAPIPByteBufferAppendStringWithFormat(buffer, ",\"maxValue\":%s", scratch_string);
if (err) {
goto error;
}
err = HAPUInt64GetDescription(uintval(stepValue), scratch_string, sizeof scratch_string);
HAPAssert(!err);
err = HAPIPByteBufferAppendStringWithFormat(buffer, ",\"minStep\":%s", scratch_string);
if (err) {
goto error;
}
}
} break;
case kHAPCharacteristicFormat_UInt64: {
const HAPUInt64Characteristic* chr = (const HAPUInt64Characteristic*) chr_;
uint64_t minimumValue = chr->constraints.minimumValue;
uint64_t maximumValue = chr->constraints.maximumValue;
uint64_t stepValue = chr->constraints.stepValue;
HAPAssert(minimumValue <= maximumValue);
if (minimumValue || maximumValue != UINT64_MAX) {
err = HAPUInt64GetDescription(uintval(minimumValue), scratch_string, sizeof scratch_string);
HAPAssert(!err);
err = HAPIPByteBufferAppendStringWithFormat(buffer, ",\"minValue\":%s", scratch_string);
if (err) {
goto error;
}
err = HAPUInt64GetDescription(uintval(maximumValue), scratch_string, sizeof scratch_string);
HAPAssert(!err);
err = HAPIPByteBufferAppendStringWithFormat(buffer, ",\"maxValue\":%s", scratch_string);
if (err) {
goto error;
}
err = HAPUInt64GetDescription(uintval(stepValue), scratch_string, sizeof scratch_string);
HAPAssert(!err);
err = HAPIPByteBufferAppendStringWithFormat(buffer, ",\"minStep\":%s", scratch_string);
if (err) {
goto error;
}
}
} break;
case kHAPCharacteristicFormat_Int: {
const HAPIntCharacteristic* chr = (const HAPIntCharacteristic*) chr_;
int32_t minimumValue = chr->constraints.minimumValue;
int32_t maximumValue = chr->constraints.maximumValue;
int32_t stepValue = chr->constraints.stepValue;
HAPAssert(minimumValue <= maximumValue);
HAPAssert(stepValue >= 0);
if (minimumValue != INT32_MIN || maximumValue != INT32_MAX) {
err = HAPIPByteBufferAppendStringWithFormat(
buffer,
",\"minValue\":%ld"
",\"maxValue\":%ld"
",\"minStep\":%ld",
(long) minimumValue,
(long) maximumValue,
(long) stepValue);
if (err) {
goto error;
}
}
} break;
case kHAPCharacteristicFormat_Float: {
const HAPFloatCharacteristic* chr = (const HAPFloatCharacteristic*) chr_;
float minimumValue = chr->constraints.minimumValue;
float maximumValue = chr->constraints.maximumValue;
float stepValue = chr->constraints.stepValue;
HAPAssert(HAPFloatIsFinite(minimumValue) || HAPFloatIsInfinite(minimumValue));
HAPAssert(HAPFloatIsFinite(maximumValue) || HAPFloatIsInfinite(maximumValue));
HAPAssert(minimumValue <= maximumValue);
HAPAssert(stepValue >= 0);
if (!(HAPFloatIsInfinite(minimumValue) && minimumValue < 0) ||
!(HAPFloatIsInfinite(maximumValue) && maximumValue > 0)) {
err = HAPJSONUtilsGetFloatDescription(minimumValue, scratch_string, sizeof scratch_string);
HAPAssert(!err);
err = HAPIPByteBufferAppendStringWithFormat(buffer, ",\"minValue\":%s", scratch_string);
if (err) {
goto error;
}
err = HAPJSONUtilsGetFloatDescription(maximumValue, scratch_string, sizeof scratch_string);
HAPAssert(!err);
err = HAPIPByteBufferAppendStringWithFormat(buffer, ",\"maxValue\":%s", scratch_string);
if (err) {
goto error;
}
err = HAPJSONUtilsGetFloatDescription(stepValue, scratch_string, sizeof scratch_string);
HAPAssert(!err);
err = HAPIPByteBufferAppendStringWithFormat(buffer, ",\"minStep\":%s", scratch_string);
if (err) {
goto error;
}
}
} break;
case kHAPCharacteristicFormat_String: {
const HAPStringCharacteristic* chr = (const HAPStringCharacteristic*) chr_;
uint16_t maxLength = chr->constraints.maxLength;
if (maxLength != 64) {
err = HAPUInt64GetDescription(uintval(maxLength), scratch_string, sizeof scratch_string);
HAPAssert(!err);
err = HAPIPByteBufferAppendStringWithFormat(buffer, ",\"maxLen\":%s", scratch_string);
if (err) {
goto error;
}
}
} break;
case kHAPCharacteristicFormat_TLV8: {
} break;
case kHAPCharacteristicFormat_Data: {
const HAPDataCharacteristic* chr = (const HAPDataCharacteristic*) chr_;
uint32_t maxLength = chr->constraints.maxLength;
if (maxLength != 2097152) {
err = HAPUInt64GetDescription(uintval(maxLength), scratch_string, sizeof scratch_string);
HAPAssert(!err);
err = HAPIPByteBufferAppendStringWithFormat(buffer, ",\"maxDataLen\":%s", scratch_string);
if (err) {
goto error;
}
}
} break;
}
}
}
HAPAssert(i == numReadContexts);
err = HAPIPByteBufferAppendStringWithFormat(buffer, "]}");
if (err) {
goto error;
}
return kHAPError_None;
error:
return kHAPError_OutOfResources;
}