HAPError HAPIPAccessoryProtocolGetCharacteristicReadResponseBytes()

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;
}