HAPError HAPIPAccessorySerializeReadResponse()

in HAP/HAPIPAccessory.c [268:1755]


HAPError HAPIPAccessorySerializeReadResponse(
        HAPIPAccessorySerializationContext* context,
        HAPAccessoryServerRef* server_,
        HAPIPSessionDescriptorRef* session,
        char* bytes,
        size_t minBytes,
        size_t maxBytes,
        size_t* numBytes) {
    HAPPrecondition(context);
    HAPPrecondition(context->state != kHAPIPAccessorySerializationState_ResponseIsComplete);
    HAPPrecondition(server_);
    HAPAccessoryServer* server = (HAPAccessoryServer*) server_;
    HAPPrecondition(server->primaryAccessory);
    HAPPrecondition(session);
    HAPPrecondition(bytes);
    HAPPrecondition(minBytes >= 1);
    HAPPrecondition(maxBytes >= minBytes);
    HAPPrecondition(numBytes);

    HAPError err;

    // See HomeKit Accessory Protocol Specification R14
    // Section 6.3 HAP Objects

    // See HomeKit Accessory Protocol Specification R14
    // Section 6.6.4 Example Accessory Attribute Database in JSON

    // For the JSON Data Interchange Format, see RFC 7159.
    // http://www.rfc-editor.org/rfc/rfc7159.txt

    char scratchBytes[64];

#define GET_CURRENT_ACCESSORY() GetCurrentAcessory(context, server_)

#define GET_CURRENT_SERVICE() GetCurrentService(context, server_)

#define GET_CURRENT_CHARACTERISTIC() ((const HAPBaseCharacteristic*) GetCurrentCharacteristic(context, server_))

#define APPEND_STRING_OR_RETURN_ERROR(string) \
    do { \
        HAPAssert(*numBytes <= maxBytes); \
        size_t numStringBytes = HAPStringGetNumBytes(string); \
        if (maxBytes - *numBytes < numStringBytes) { \
            HAPLogError(&logObject, "Not enough resources to serialize GET /accessories response."); \
            return kHAPError_OutOfResources; \
        } \
        HAPRawBufferCopyBytes(&bytes[*numBytes], string, numStringBytes); \
        *numBytes += numStringBytes; \
        HAPAssert(*numBytes <= maxBytes); \
    } while (0)

#define APPEND_UUID_OR_RETURN_ERROR(uuid) \
    do { \
        HAPAssert(*numBytes <= maxBytes); \
        HAPAssert(sizeof scratchBytes >= 2); \
        err = HAPUUIDGetDescription(uuid, &scratchBytes[1], sizeof scratchBytes - 2); \
        HAPAssert(!err); \
        size_t numScratchBytes = HAPStringGetNumBytes(&scratchBytes[1]) + 2; \
        scratchBytes[0] = '"'; \
        scratchBytes[numScratchBytes - 1] = '"'; \
        if (maxBytes - *numBytes < numScratchBytes) { \
            HAPLogError(&logObject, "Not enough resources to serialize GET /accessories response."); \
            return kHAPError_OutOfResources; \
        } \
        HAPRawBufferCopyBytes(&bytes[*numBytes], scratchBytes, numScratchBytes); \
        *numBytes += numScratchBytes; \
        HAPAssert(*numBytes <= maxBytes); \
    } while (0)

#define APPEND_UINT64_OR_RETURN_ERROR(value) \
    do { \
        HAPAssert(*numBytes <= maxBytes); \
        err = HAPUInt64GetDescription(value, scratchBytes, sizeof scratchBytes); \
        HAPAssert(!err); \
        size_t numScratchBytes = HAPStringGetNumBytes(scratchBytes); \
        if (maxBytes - *numBytes < numScratchBytes) { \
            HAPLogError(&logObject, "Not enough resources to serialize GET /accessories response."); \
            return kHAPError_OutOfResources; \
        } \
        HAPRawBufferCopyBytes(&bytes[*numBytes], scratchBytes, numScratchBytes); \
        *numBytes += numScratchBytes; \
        HAPAssert(*numBytes <= maxBytes); \
    } while (0)

#define APPEND_INT32_OR_RETURN_ERROR(value) \
    do { \
        HAPAssert(*numBytes <= maxBytes); \
        err = HAPStringWithFormat(scratchBytes, sizeof scratchBytes, "%ld", (long) value); \
        HAPAssert(!err); \
        size_t numScratchBytes = HAPStringGetNumBytes(scratchBytes); \
        if (maxBytes - *numBytes < numScratchBytes) { \
            HAPLogError(&logObject, "Not enough resources to serialize GET /accessories response."); \
            return kHAPError_OutOfResources; \
        } \
        HAPRawBufferCopyBytes(&bytes[*numBytes], scratchBytes, numScratchBytes); \
        *numBytes += numScratchBytes; \
        HAPAssert(*numBytes <= maxBytes); \
    } while (0)

#define APPEND_FLOAT_OR_RETURN_ERROR(value) \
    do { \
        HAPAssert(*numBytes <= maxBytes); \
        err = HAPJSONUtilsGetFloatDescription(value, scratchBytes, sizeof scratchBytes); \
        HAPAssert(!err); \
        size_t numScratchBytes = HAPStringGetNumBytes(scratchBytes); \
        if (maxBytes - *numBytes < numScratchBytes) { \
            HAPLogError(&logObject, "Not enough resources to serialize GET /accessories response."); \
            return kHAPError_OutOfResources; \
        } \
        HAPRawBufferCopyBytes(&bytes[*numBytes], scratchBytes, numScratchBytes); \
        *numBytes += numScratchBytes; \
        HAPAssert(*numBytes <= maxBytes); \
    } while (0)

    *numBytes = 0;

    do {
        HAPAssert(sizeof context->state == sizeof(HAPIPAccessorySerializationState));
        switch ((HAPIPAccessorySerializationState) context->state) {
            case kHAPIPAccessorySerializationState_ResponseObject_Begin: {
                APPEND_STRING_OR_RETURN_ERROR("{");
                context->state = kHAPIPAccessorySerializationState_AccessoriesArray_Name;
            }
                continue;
            case kHAPIPAccessorySerializationState_ResponseObject_End: {
                APPEND_STRING_OR_RETURN_ERROR("}");
                context->state = kHAPIPAccessorySerializationState_ResponseIsComplete;
            }
                continue;
            case kHAPIPAccessorySerializationState_AccessoriesArray_Name: {
                APPEND_STRING_OR_RETURN_ERROR("\"accessories\"");
                context->state = kHAPIPAccessorySerializationState_AccessoriesArray_NameSeparator;
            }
                continue;
            case kHAPIPAccessorySerializationState_AccessoriesArray_NameSeparator: {
                APPEND_STRING_OR_RETURN_ERROR(":");
                context->state = kHAPIPAccessorySerializationState_AccessoriesArray_Begin;
            }
                continue;
            case kHAPIPAccessorySerializationState_AccessoriesArray_Begin: {
                APPEND_STRING_OR_RETURN_ERROR("[");
                context->accessoryIndex = 0;
                context->state = kHAPIPAccessorySerializationState_AccessoryObject_Begin;
            }
                continue;
            case kHAPIPAccessorySerializationState_AccessoriesArray_End: {
                APPEND_STRING_OR_RETURN_ERROR("]");
                context->state = kHAPIPAccessorySerializationState_ResponseObject_End;
            }
                continue;
            case kHAPIPAccessorySerializationState_AccessoryObject_Begin: {
                APPEND_STRING_OR_RETURN_ERROR("{");
                context->state = kHAPIPAccessorySerializationState_AccessoryID_Name;
            }
                continue;
            case kHAPIPAccessorySerializationState_AccessoryObject_End: {
                APPEND_STRING_OR_RETURN_ERROR("}");
                HAPAssert(context->accessoryIndex < UINT8_MAX);
                context->accessoryIndex++;
                if (context->accessoryIndex == 1) {
                    if (server->ip.bridgedAccessories && server->ip.bridgedAccessories[0]) {
                        context->state = kHAPIPAccessorySerializationState_AccessoryObject_Separator;
                    } else {
                        context->state = kHAPIPAccessorySerializationState_AccessoriesArray_End;
                    }
                } else {
                    HAPAssert(server->ip.bridgedAccessories);
                    if (server->ip.bridgedAccessories[context->accessoryIndex - 1]) {
                        context->state = kHAPIPAccessorySerializationState_AccessoryObject_Separator;
                    } else {
                        context->state = kHAPIPAccessorySerializationState_AccessoriesArray_End;
                    }
                }
            }
                continue;
            case kHAPIPAccessorySerializationState_AccessoryObject_Separator: {
                APPEND_STRING_OR_RETURN_ERROR(",");
                context->state = kHAPIPAccessorySerializationState_AccessoryObject_Begin;
            }
                continue;
            case kHAPIPAccessorySerializationState_AccessoryID_Name: {
                APPEND_STRING_OR_RETURN_ERROR("\"aid\"");
                context->state = kHAPIPAccessorySerializationState_AccessoryID_NameSeparator;
            }
                continue;
            case kHAPIPAccessorySerializationState_AccessoryID_NameSeparator: {
                APPEND_STRING_OR_RETURN_ERROR(":");
                context->state = kHAPIPAccessorySerializationState_AccessoryID_Value;
            }
                continue;
            case kHAPIPAccessorySerializationState_AccessoryID_Value: {
                const HAPAccessory* accessory = GET_CURRENT_ACCESSORY();
                HAPAssert(accessory);
                APPEND_UINT64_OR_RETURN_ERROR(accessory->aid);
                context->state = kHAPIPAccessorySerializationState_AccessoryID_ValueSeparator;
            }
                continue;
            case kHAPIPAccessorySerializationState_AccessoryID_ValueSeparator: {
                APPEND_STRING_OR_RETURN_ERROR(",");
                context->state = kHAPIPAccessorySerializationState_ServicesArray_Name;
            }
                continue;
            case kHAPIPAccessorySerializationState_ServicesArray_Name: {
                APPEND_STRING_OR_RETURN_ERROR("\"services\"");
                context->state = kHAPIPAccessorySerializationState_ServicesArray_NameSeparator;
            }
                continue;
            case kHAPIPAccessorySerializationState_ServicesArray_NameSeparator: {
                APPEND_STRING_OR_RETURN_ERROR(":");
                context->state = kHAPIPAccessorySerializationState_ServicesArray_Begin;
            }
                continue;
            case kHAPIPAccessorySerializationState_ServicesArray_Begin: {
                APPEND_STRING_OR_RETURN_ERROR("[");
                const HAPAccessory* accessory = GET_CURRENT_ACCESSORY();
                HAPAssert(accessory);
                const HAPService* const* services = accessory->services;
                HAPAssert(services);
                context->serviceIndex = 0;
                const HAPService* service = services[context->serviceIndex];
                while (service && !HAPAccessoryServerSupportsService(server_, kHAPTransportType_IP, service)) {
                    HAPAssert(context->serviceIndex < UINT8_MAX);
                    context->serviceIndex++;
                    service = services[context->serviceIndex];
                }
                if (service) {
                    context->state = kHAPIPAccessorySerializationState_ServiceObject_Begin;
                } else {
                    context->state = kHAPIPAccessorySerializationState_ServicesArray_End;
                }
            }
                continue;
            case kHAPIPAccessorySerializationState_ServicesArray_End: {
                APPEND_STRING_OR_RETURN_ERROR("]");
                context->state = kHAPIPAccessorySerializationState_AccessoryObject_End;
            }
                continue;
            case kHAPIPAccessorySerializationState_ServiceObject_Begin: {
                APPEND_STRING_OR_RETURN_ERROR("{");
                context->state = kHAPIPAccessorySerializationState_ServiceID_Name;
            }
                continue;
            case kHAPIPAccessorySerializationState_ServiceObject_End: {
                APPEND_STRING_OR_RETURN_ERROR("}");
                const HAPAccessory* accessory = GET_CURRENT_ACCESSORY();
                HAPAssert(accessory);
                const HAPService* const* services = accessory->services;
                HAPAssert(services);
                const HAPService* service;
                do {
                    HAPAssert(context->serviceIndex < UINT8_MAX);
                    context->serviceIndex++;
                    service = services[context->serviceIndex];
                } while (service && !HAPAccessoryServerSupportsService(server_, kHAPTransportType_IP, service));
                if (service) {
                    context->state = kHAPIPAccessorySerializationState_ServiceObject_Separator;
                } else {
                    context->state = kHAPIPAccessorySerializationState_ServicesArray_End;
                }
            }
                continue;
            case kHAPIPAccessorySerializationState_ServiceObject_Separator: {
                APPEND_STRING_OR_RETURN_ERROR(",");
                context->state = kHAPIPAccessorySerializationState_ServiceObject_Begin;
            }
                continue;
            case kHAPIPAccessorySerializationState_ServiceID_Name: {
                APPEND_STRING_OR_RETURN_ERROR("\"iid\"");
                context->state = kHAPIPAccessorySerializationState_ServiceID_NameSeparator;
            }
                continue;
            case kHAPIPAccessorySerializationState_ServiceID_NameSeparator: {
                APPEND_STRING_OR_RETURN_ERROR(":");
                context->state = kHAPIPAccessorySerializationState_ServiceID_Value;
            }
                continue;
            case kHAPIPAccessorySerializationState_ServiceID_Value: {
                const HAPService* service = GET_CURRENT_SERVICE();
                HAPAssert(service);
                APPEND_UINT64_OR_RETURN_ERROR(service->iid);
                context->state = kHAPIPAccessorySerializationState_ServiceID_ValueSeparator;
            }
                continue;
            case kHAPIPAccessorySerializationState_ServiceID_ValueSeparator: {
                APPEND_STRING_OR_RETURN_ERROR(",");
                context->state = kHAPIPAccessorySerializationState_ServiceType_Name;
            }
                continue;
            case kHAPIPAccessorySerializationState_ServiceType_Name: {
                APPEND_STRING_OR_RETURN_ERROR("\"type\"");
                context->state = kHAPIPAccessorySerializationState_ServiceType_NameSeparator;
            }
                continue;
            case kHAPIPAccessorySerializationState_ServiceType_NameSeparator: {
                APPEND_STRING_OR_RETURN_ERROR(":");
                context->state = kHAPIPAccessorySerializationState_ServiceType_Value;
            }
                continue;
            case kHAPIPAccessorySerializationState_ServiceType_Value: {
                const HAPService* service = GET_CURRENT_SERVICE();
                HAPAssert(service);
                APPEND_UUID_OR_RETURN_ERROR(service->serviceType);
                context->state = kHAPIPAccessorySerializationState_ServiceType_ValueSeparator;
            }
                continue;
            case kHAPIPAccessorySerializationState_ServiceType_ValueSeparator: {
                APPEND_STRING_OR_RETURN_ERROR(",");
                context->state = kHAPIPAccessorySerializationState_ServicePropertyPrimary_Name;
            }
                continue;
            case kHAPIPAccessorySerializationState_ServicePropertyPrimary_Name: {
                APPEND_STRING_OR_RETURN_ERROR("\"primary\"");
                context->state = kHAPIPAccessorySerializationState_ServicePropertyPrimary_NameSeparator;
            }
                continue;
            case kHAPIPAccessorySerializationState_ServicePropertyPrimary_NameSeparator: {
                APPEND_STRING_OR_RETURN_ERROR(":");
                context->state = kHAPIPAccessorySerializationState_ServicePropertyPrimary_Value;
            }
                continue;
            case kHAPIPAccessorySerializationState_ServicePropertyPrimary_Value: {
                const HAPService* service = GET_CURRENT_SERVICE();
                HAPAssert(service);
                APPEND_STRING_OR_RETURN_ERROR(service->properties.primaryService ? "true" : "false");
                context->state = kHAPIPAccessorySerializationState_ServicePropertyPrimary_ValueSeparator;
            }
                continue;
            case kHAPIPAccessorySerializationState_ServicePropertyPrimary_ValueSeparator: {
                APPEND_STRING_OR_RETURN_ERROR(",");
                context->state = kHAPIPAccessorySerializationState_ServicePropertyHidden_Name;
            }
                continue;
            case kHAPIPAccessorySerializationState_ServicePropertyHidden_Name: {
                APPEND_STRING_OR_RETURN_ERROR("\"hidden\"");
                context->state = kHAPIPAccessorySerializationState_ServicePropertyHidden_NameSeparator;
            }
                continue;
            case kHAPIPAccessorySerializationState_ServicePropertyHidden_NameSeparator: {
                APPEND_STRING_OR_RETURN_ERROR(":");
                context->state = kHAPIPAccessorySerializationState_ServicePropertyHidden_Value;
            }
                continue;
            case kHAPIPAccessorySerializationState_ServicePropertyHidden_Value: {
                const HAPService* service = GET_CURRENT_SERVICE();
                HAPAssert(service);
                APPEND_STRING_OR_RETURN_ERROR(service->properties.hidden ? "true" : "false");
                context->state = kHAPIPAccessorySerializationState_ServicePropertyHidden_ValueSeparator;
            }
                continue;
            case kHAPIPAccessorySerializationState_ServicePropertyHidden_ValueSeparator: {
                APPEND_STRING_OR_RETURN_ERROR(",");
                context->state = kHAPIPAccessorySerializationState_LinkedServicesArray_Name;
            }
                continue;
            case kHAPIPAccessorySerializationState_LinkedServicesArray_Name: {
                APPEND_STRING_OR_RETURN_ERROR("\"linked\"");
                context->state = kHAPIPAccessorySerializationState_LinkedServicesArray_NameSeparator;
            }
                continue;
            case kHAPIPAccessorySerializationState_LinkedServicesArray_NameSeparator: {
                APPEND_STRING_OR_RETURN_ERROR(":");
                context->state = kHAPIPAccessorySerializationState_LinkedServicesArray_Begin;
            }
                continue;
            case kHAPIPAccessorySerializationState_LinkedServicesArray_Begin: {
                APPEND_STRING_OR_RETURN_ERROR("[");
                const HAPService* service = GET_CURRENT_SERVICE();
                HAPAssert(service);
                const uint16_t* linkedServices = service->linkedServices;
                if (linkedServices) {
                    context->index = 0;
                    if (linkedServices[context->index]) {
                        context->state = kHAPIPAccessorySerializationState_LinkedServiceID_Value;
                    } else {
                        context->state = kHAPIPAccessorySerializationState_LinkedServicesArray_End;
                    }
                } else {
                    context->state = kHAPIPAccessorySerializationState_LinkedServicesArray_End;
                }
            }
                continue;
            case kHAPIPAccessorySerializationState_LinkedServicesArray_End: {
                APPEND_STRING_OR_RETURN_ERROR("]");
                context->state = kHAPIPAccessorySerializationState_LinkedServicesArray_ValueSeparator;
            }
                continue;
            case kHAPIPAccessorySerializationState_LinkedServicesArray_ValueSeparator: {
                APPEND_STRING_OR_RETURN_ERROR(",");
                context->state = kHAPIPAccessorySerializationState_CharacteristicsArray_Name;
            }
                continue;
            case kHAPIPAccessorySerializationState_LinkedServiceID_Value: {
                const HAPService* service = GET_CURRENT_SERVICE();
                HAPAssert(service);
                const uint16_t* linkedServices = service->linkedServices;
                HAPAssert(linkedServices);
                HAPAssert(linkedServices[context->index]);
                APPEND_UINT64_OR_RETURN_ERROR(linkedServices[context->index]);
                HAPAssert(context->index < UINT8_MAX);
                context->index++;
                if (linkedServices[context->index]) {
                    context->state = kHAPIPAccessorySerializationState_LinkedServiceID_Separator;
                } else {
                    context->state = kHAPIPAccessorySerializationState_LinkedServicesArray_End;
                }
            }
                continue;
            case kHAPIPAccessorySerializationState_LinkedServiceID_Separator: {
                APPEND_STRING_OR_RETURN_ERROR(",");
                context->state = kHAPIPAccessorySerializationState_LinkedServiceID_Value;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicsArray_Name: {
                APPEND_STRING_OR_RETURN_ERROR("\"characteristics\"");
                context->state = kHAPIPAccessorySerializationState_CharacteristicsArray_NameSeparator;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicsArray_NameSeparator: {
                APPEND_STRING_OR_RETURN_ERROR(":");
                context->state = kHAPIPAccessorySerializationState_CharacteristicsArray_Begin;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicsArray_Begin: {
                APPEND_STRING_OR_RETURN_ERROR("[");
                const HAPService* service = GET_CURRENT_SERVICE();
                HAPAssert(service);
                const HAPCharacteristic* const* characteristics = service->characteristics;
                HAPAssert(characteristics);
                context->characteristicIndex = 0;
                const HAPBaseCharacteristic* characteristic = characteristics[context->characteristicIndex];
                while (characteristic && !HAPIPCharacteristicIsSupported(characteristic)) {
                    HAPAssert(context->characteristicIndex < UINT8_MAX);
                    context->characteristicIndex++;
                    characteristic = characteristics[context->characteristicIndex];
                }
                if (characteristic) {
                    context->state = kHAPIPAccessorySerializationState_CharacteristicsObject_Begin;
                } else {
                    context->state = kHAPIPAccessorySerializationState_CharacteristicsArray_End;
                }
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicsArray_End: {
                APPEND_STRING_OR_RETURN_ERROR("]");
                context->state = kHAPIPAccessorySerializationState_ServiceObject_End;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicsObject_Begin: {
                APPEND_STRING_OR_RETURN_ERROR("{");
                context->state = kHAPIPAccessorySerializationState_CharacteristicID_Name;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicObject_End: {
                APPEND_STRING_OR_RETURN_ERROR("}");
                const HAPService* service = GET_CURRENT_SERVICE();
                HAPAssert(service);
                const HAPCharacteristic* const* characteristics = service->characteristics;
                HAPAssert(characteristics);
                const HAPBaseCharacteristic* characteristic;
                do {
                    HAPAssert(context->characteristicIndex < UINT8_MAX);
                    context->characteristicIndex++;
                    characteristic = characteristics[context->characteristicIndex];
                } while (characteristic && !HAPIPCharacteristicIsSupported(characteristic));
                if (characteristic) {
                    context->state = kHAPIPAccessorySerializationState_CharacteristicObject_Separator;
                } else {
                    context->state = kHAPIPAccessorySerializationState_CharacteristicsArray_End;
                }
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicObject_Separator: {
                APPEND_STRING_OR_RETURN_ERROR(",");
                context->state = kHAPIPAccessorySerializationState_CharacteristicsObject_Begin;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicID_Name: {
                APPEND_STRING_OR_RETURN_ERROR("\"iid\"");
                context->state = kHAPIPAccessorySerializationState_CharacteristicID_NameSeparator;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicID_NameSeparator: {
                APPEND_STRING_OR_RETURN_ERROR(":");
                context->state = kHAPIPAccessorySerializationState_CharacteristicID_Value;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicID_Value: {
                const HAPBaseCharacteristic* baseCharacteristic = GET_CURRENT_CHARACTERISTIC();
                HAPAssert(baseCharacteristic);
                APPEND_UINT64_OR_RETURN_ERROR(baseCharacteristic->iid);
                context->state = kHAPIPAccessorySerializationState_CharacteristicID_ValueSeparator;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicID_ValueSeparator: {
                APPEND_STRING_OR_RETURN_ERROR(",");
                context->state = kHAPIPAccessorySerializationState_CharacteristicType_Name;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicType_Name: {
                APPEND_STRING_OR_RETURN_ERROR("\"type\"");
                context->state = kHAPIPAccessorySerializationState_CharacteristicType_NameSeparator;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicType_NameSeparator: {
                APPEND_STRING_OR_RETURN_ERROR(":");
                context->state = kHAPIPAccessorySerializationState_CharacteristicType_Value;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicType_Value: {
                const HAPBaseCharacteristic* baseCharacteristic = GET_CURRENT_CHARACTERISTIC();
                HAPAssert(baseCharacteristic);
                APPEND_UUID_OR_RETURN_ERROR(baseCharacteristic->characteristicType);
                context->state = kHAPIPAccessorySerializationState_CharacteristicType_ValueSeparator;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicType_ValueSeparator: {
                APPEND_STRING_OR_RETURN_ERROR(",");
                context->state = kHAPIPAccessorySerializationState_CharacteristicFormat_Name;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicFormat_Name: {
                APPEND_STRING_OR_RETURN_ERROR("\"format\"");
                context->state = kHAPIPAccessorySerializationState_CharacteristicFormat_NameSeparator;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicFormat_NameSeparator: {
                APPEND_STRING_OR_RETURN_ERROR(":");
                context->state = kHAPIPAccessorySerializationState_CharacteristicFormat_Value;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicFormat_Value: {
                const HAPBaseCharacteristic* baseCharacteristic = GET_CURRENT_CHARACTERISTIC();
                HAPAssert(baseCharacteristic);
                switch (baseCharacteristic->format) {
                    case kHAPCharacteristicFormat_Bool: {
                        APPEND_STRING_OR_RETURN_ERROR("\"bool\"");
                    } break;
                    case kHAPCharacteristicFormat_UInt8: {
                        APPEND_STRING_OR_RETURN_ERROR("\"uint8\"");
                    } break;
                    case kHAPCharacteristicFormat_UInt16: {
                        APPEND_STRING_OR_RETURN_ERROR("\"uint16\"");
                    } break;
                    case kHAPCharacteristicFormat_UInt32: {
                        APPEND_STRING_OR_RETURN_ERROR("\"uint32\"");
                    } break;
                    case kHAPCharacteristicFormat_UInt64: {
                        APPEND_STRING_OR_RETURN_ERROR("\"uint64\"");
                    } break;
                    case kHAPCharacteristicFormat_Int: {
                        APPEND_STRING_OR_RETURN_ERROR("\"int\"");
                    } break;
                    case kHAPCharacteristicFormat_Float: {
                        APPEND_STRING_OR_RETURN_ERROR("\"float\"");
                    } break;
                    case kHAPCharacteristicFormat_String: {
                        APPEND_STRING_OR_RETURN_ERROR("\"string\"");
                    } break;
                    case kHAPCharacteristicFormat_TLV8: {
                        APPEND_STRING_OR_RETURN_ERROR("\"tlv8\"");
                    } break;
                    case kHAPCharacteristicFormat_Data: {
                        APPEND_STRING_OR_RETURN_ERROR("\"data\"");
                    } break;
                }
                context->state = kHAPIPAccessorySerializationState_CharacteristicFormat_ValueSeparator;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicFormat_ValueSeparator: {
                APPEND_STRING_OR_RETURN_ERROR(",");
                const HAPBaseCharacteristic* baseCharacteristic = GET_CURRENT_CHARACTERISTIC();
                HAPAssert(baseCharacteristic);
                if (baseCharacteristic->properties.readable) {
                    context->state = kHAPIPAccessorySerializationState_CharacteristicValue_Name;
                } else {
                    context->state = kHAPIPAccessorySerializationState_CharacteristicPermissionsArray_Name;
                }
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicValue_Name: {
                APPEND_STRING_OR_RETURN_ERROR("\"value\"");
                context->state = kHAPIPAccessorySerializationState_CharacteristicValue_NameSeparator;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicValue_NameSeparator: {
                APPEND_STRING_OR_RETURN_ERROR(":");
                context->state = kHAPIPAccessorySerializationState_CharacteristicValue_Value;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicValue_Value: {
                const HAPBaseCharacteristic* baseCharacteristic = GET_CURRENT_CHARACTERISTIC();
                HAPAssert(baseCharacteristic);
                HAPAssert(baseCharacteristic->properties.readable);
                HAPIPSessionReadResult readResult;

                HAPAssert(*numBytes <= maxBytes);
                if (maxBytes - *numBytes < 2) {
                    HAPLogError(&logObject, "Not enough resources to serialize GET /accessories response.");
                    return kHAPError_OutOfResources;
                }
                // Buffer 'bytes' has enough capacity to store at least an empty string including quotation marks.

                HAPIPByteBuffer dataBuffer;
                dataBuffer.data = &bytes[*numBytes + 1]; // Leave space for beginning quotation mark.
                dataBuffer.position = 0;
                dataBuffer.limit = maxBytes - *numBytes - 2; // Leave space for ending quotation mark.
                dataBuffer.capacity = dataBuffer.limit;
                HAPAssert(dataBuffer.data);
                HAPAssert(dataBuffer.position <= dataBuffer.limit);
                HAPAssert(dataBuffer.limit <= dataBuffer.capacity);

                const HAPAccessory* accessory = GET_CURRENT_ACCESSORY();
                HAPAssert(accessory);
                const HAPService* service = GET_CURRENT_SERVICE();
                HAPAssert(service);
                HAPIPSessionHandleReadRequest(
                        session,
                        kHAPIPSessionContext_GetAccessories,
                        baseCharacteristic,
                        service,
                        accessory,
                        &readResult,
                        &dataBuffer);
                if (HAPUUIDAreEqual(
                            baseCharacteristic->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
                    HAPLogCharacteristicInfo(
                            &logObject,
                            baseCharacteristic,
                            service,
                            accessory,
                            "Sending null value (readHandler callback is only called for HAP events).");
                    APPEND_STRING_OR_RETURN_ERROR("null");
                } else if (
                        baseCharacteristic->properties.ip.controlPoint &&
                        (baseCharacteristic->format == kHAPCharacteristicFormat_TLV8)) {
                    APPEND_STRING_OR_RETURN_ERROR("\"\"");
                } else if (readResult.status != 0) {
                    if (baseCharacteristic->format == kHAPCharacteristicFormat_TLV8) {
                        HAPLogCharacteristicInfo(
                                &logObject,
                                baseCharacteristic,
                                service,
                                accessory,
                                "Read handler failed with error. Sending empty TLV value.");
                        APPEND_STRING_OR_RETURN_ERROR("\"\"");
                    } else {
                        APPEND_STRING_OR_RETURN_ERROR("null");
                    }
                } else {
                    switch (baseCharacteristic->format) {
                        case kHAPCharacteristicFormat_Bool: {
                            APPEND_STRING_OR_RETURN_ERROR(readResult.value.unsignedIntValue ? "1" : "0");
                        } break;
                        case kHAPCharacteristicFormat_UInt8:
                        case kHAPCharacteristicFormat_UInt16:
                        case kHAPCharacteristicFormat_UInt32:
                        case kHAPCharacteristicFormat_UInt64: {
                            APPEND_UINT64_OR_RETURN_ERROR(readResult.value.unsignedIntValue);
                        } break;
                        case kHAPCharacteristicFormat_Int: {
                            APPEND_INT32_OR_RETURN_ERROR(readResult.value.intValue);
                        } break;
                        case kHAPCharacteristicFormat_Float: {
                            APPEND_FLOAT_OR_RETURN_ERROR(readResult.value.floatValue);
                        } break;
                        case kHAPCharacteristicFormat_String:
                        case kHAPCharacteristicFormat_TLV8:
                        case kHAPCharacteristicFormat_Data: {
                            err = HAPJSONUtilsEscapeStringData(
                                    HAPNonnull(readResult.value.stringValue.bytes),
                                    dataBuffer.limit,
                                    &readResult.value.stringValue.numBytes);
                            if (err) {
                                HAPAssert(err == kHAPError_OutOfResources);
                                HAPLogError(&logObject, "Not enough resources to serialize GET /accessories response.");
                                return err;
                            }
                            bytes[*numBytes] = '"';
                            bytes[*numBytes + 1 + readResult.value.stringValue.numBytes] = '"';
                            *numBytes += 1 + readResult.value.stringValue.numBytes + 1;
                        } break;
                    }
                }

                HAPAssert(*numBytes <= maxBytes);

                context->state = kHAPIPAccessorySerializationState_CharacteristicValue_ValueSeparator;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicValue_ValueSeparator: {
                APPEND_STRING_OR_RETURN_ERROR(",");
                context->state = kHAPIPAccessorySerializationState_CharacteristicPermissionsArray_Name;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicPermissionsArray_Name: {
                APPEND_STRING_OR_RETURN_ERROR("\"perms\"");
                context->state = kHAPIPAccessorySerializationState_CharacteristicPermissionsArray_NameSeparator;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicPermissionsArray_NameSeparator: {
                APPEND_STRING_OR_RETURN_ERROR(":");
                context->state = kHAPIPAccessorySerializationState_CharacteristicPermissionsArray_Begin;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicPermissionsArray_Begin: {
                const HAPBaseCharacteristic* baseCharacteristic = GET_CURRENT_CHARACTERISTIC();
                HAPAssert(baseCharacteristic);
                APPEND_STRING_OR_RETURN_ERROR("[");
                context->index = 0;
                if (context->index < HAPCharacteristicGetNumEnabledProperties(baseCharacteristic)) {
                    context->state = kHAPIPAccessorySerializationState_CharacteristicPermission_Value;
                } else {
                    context->state = kHAPIPAccessorySerializationState_CharacteristicPermissionsArray_End;
                }
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicPermissionsArray_End: {
                APPEND_STRING_OR_RETURN_ERROR("]");
                context->state = kHAPIPAccessorySerializationState_CharacteristicPermissionsArray_ValueSeparator;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicPermissionsArray_ValueSeparator: {
                const HAPBaseCharacteristic* baseCharacteristic = GET_CURRENT_CHARACTERISTIC();
                HAPAssert(baseCharacteristic);
                if (baseCharacteristic->properties.readable) {
                    APPEND_STRING_OR_RETURN_ERROR(",");
                    context->state = kHAPIPAccessorySerializationState_CharacteristicEventNotifications_Name;
                } else if (baseCharacteristic->manufacturerDescription) {
                    APPEND_STRING_OR_RETURN_ERROR(",");
                    context->state = kHAPIPAccessorySerializationState_CharacteristicDescription_Name;
                } else if (HAPCharacteristicGetUnit(baseCharacteristic) != kHAPCharacteristicUnits_None) {
                    APPEND_STRING_OR_RETURN_ERROR(",");
                    context->state = kHAPIPAccessorySerializationState_CharacteristicUnit_Name;
                } else {
                    context->state = kHAPIPAccessorySerializationState_CharacteristicUnit_ValueSeparator;
                }
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicPermission_Value: {
                const HAPBaseCharacteristic* baseCharacteristic = GET_CURRENT_CHARACTERISTIC();
                HAPAssert(baseCharacteristic);
                size_t numEnabledProperties = HAPCharacteristicGetNumEnabledProperties(baseCharacteristic);
                HAPAssert(context->index < numEnabledProperties);
                uint8_t i = 0;
                if (baseCharacteristic->properties.readable) {
                    if (i == context->index) {
                        APPEND_STRING_OR_RETURN_ERROR("\"pr\"");
                    }
                    i++;
                }
                if (baseCharacteristic->properties.writable) {
                    if (i == context->index) {
                        APPEND_STRING_OR_RETURN_ERROR("\"pw\"");
                    }
                    i++;
                }
                if (baseCharacteristic->properties.supportsEventNotification) {
                    if (i == context->index) {
                        APPEND_STRING_OR_RETURN_ERROR("\"ev\"");
                    }
                    i++;
                }
                if (baseCharacteristic->properties.supportsAuthorizationData) {
                    if (i == context->index) {
                        APPEND_STRING_OR_RETURN_ERROR("\"aa\"");
                    }
                    i++;
                }
                if (baseCharacteristic->properties.requiresTimedWrite) {
                    if (i == context->index) {
                        APPEND_STRING_OR_RETURN_ERROR("\"tw\"");
                    }
                    i++;
                }
                if (baseCharacteristic->properties.ip.supportsWriteResponse) {
                    if (i == context->index) {
                        APPEND_STRING_OR_RETURN_ERROR("\"wr\"");
                    }
                    i++;
                }
                if (baseCharacteristic->properties.hidden) {
                    if (i == context->index) {
                        APPEND_STRING_OR_RETURN_ERROR("\"hd\"");
                    }
                    i++;
                }
                HAPAssert(i == numEnabledProperties);
                HAPAssert(context->index < UINT8_MAX);
                context->index++;
                if (context->index < numEnabledProperties) {
                    context->state = kHAPIPAccessorySerializationState_CharacteristicPermission_Separator;
                } else {
                    context->state = kHAPIPAccessorySerializationState_CharacteristicPermissionsArray_End;
                }
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicPermission_Separator: {
                APPEND_STRING_OR_RETURN_ERROR(",");
                context->state = kHAPIPAccessorySerializationState_CharacteristicPermission_Value;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicEventNotifications_Name: {
                APPEND_STRING_OR_RETURN_ERROR("\"ev\"");
                context->state = kHAPIPAccessorySerializationState_CharacteristicEventNotifications_NameSeparator;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicEventNotifications_NameSeparator: {
                APPEND_STRING_OR_RETURN_ERROR(":");
                context->state = kHAPIPAccessorySerializationState_CharacteristicEventNotifications_Value;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicEventNotifications_Value: {
                const HAPAccessory* accessory = GET_CURRENT_ACCESSORY();
                HAPAssert(accessory);
                const HAPService* service = GET_CURRENT_SERVICE();
                HAPAssert(service);
                const HAPBaseCharacteristic* baseCharacteristic = GET_CURRENT_CHARACTERISTIC();
                HAPAssert(baseCharacteristic);
                APPEND_STRING_OR_RETURN_ERROR(
                        HAPIPSessionAreEventNotificationsEnabled(session, baseCharacteristic, service, accessory) ?
                                "true" :
                                "false");
                context->state = kHAPIPAccessorySerializationState_CharacteristicEventNotifications_ValueSeparator;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicEventNotifications_ValueSeparator: {
                const HAPBaseCharacteristic* baseCharacteristic = GET_CURRENT_CHARACTERISTIC();
                HAPAssert(baseCharacteristic);
                if (baseCharacteristic->manufacturerDescription) {
                    APPEND_STRING_OR_RETURN_ERROR(",");
                    context->state = kHAPIPAccessorySerializationState_CharacteristicDescription_Name;
                } else if (HAPCharacteristicGetUnit(baseCharacteristic) != kHAPCharacteristicUnits_None) {
                    APPEND_STRING_OR_RETURN_ERROR(",");
                    context->state = kHAPIPAccessorySerializationState_CharacteristicUnit_Name;
                } else {
                    context->state = kHAPIPAccessorySerializationState_CharacteristicUnit_ValueSeparator;
                }
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicDescription_Name: {
                APPEND_STRING_OR_RETURN_ERROR("\"description\"");
                context->state = kHAPIPAccessorySerializationState_CharacteristicDescription_NameSeparator;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicDescription_NameSeparator: {
                APPEND_STRING_OR_RETURN_ERROR(":");
                context->state = kHAPIPAccessorySerializationState_CharacteristicDescription_Value;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicDescription_Value: {
                const HAPBaseCharacteristic* baseCharacteristic = GET_CURRENT_CHARACTERISTIC();
                HAPAssert(baseCharacteristic);
                HAPAssert(baseCharacteristic->manufacturerDescription);

                HAPAssert(*numBytes <= maxBytes);
                if (maxBytes - *numBytes < 2) {
                    HAPLogError(&logObject, "Not enough resources to serialize GET /accessories response.");
                    return kHAPError_OutOfResources;
                }
                // Buffer 'bytes' has enough capacity to store at least an empty string including quotation marks.

                const char* manufacturerDescription = HAPNonnull(baseCharacteristic->manufacturerDescription);
                size_t numManufacturerDescriptionBytes = HAPStringGetNumBytes(manufacturerDescription);
                if (maxBytes - *numBytes - 2 < numManufacturerDescriptionBytes) {
                    HAPLogError(&logObject, "Not enough resources to serialize GET /accessories response.");
                    return kHAPError_OutOfResources;
                }
                HAPRawBufferCopyBytes(&bytes[*numBytes + 1], manufacturerDescription, numManufacturerDescriptionBytes);
                err = HAPJSONUtilsEscapeStringData(
                        &bytes[*numBytes + 1], maxBytes - *numBytes - 2, &numManufacturerDescriptionBytes);
                if (err) {
                    HAPAssert(err == kHAPError_OutOfResources);
                    HAPLogError(&logObject, "Not enough resources to serialize GET /accessories response.");
                    return err;
                }
                bytes[*numBytes] = '"';
                bytes[*numBytes + 1 + numManufacturerDescriptionBytes] = '"';
                *numBytes += 1 + numManufacturerDescriptionBytes + 1;

                HAPAssert(*numBytes <= maxBytes);

                context->state = kHAPIPAccessorySerializationState_CharacteristicDescription_ValueSeparator;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicDescription_ValueSeparator: {
                const HAPBaseCharacteristic* baseCharacteristic = GET_CURRENT_CHARACTERISTIC();
                HAPAssert(baseCharacteristic);
                if (HAPCharacteristicGetUnit(baseCharacteristic) != kHAPCharacteristicUnits_None) {
                    APPEND_STRING_OR_RETURN_ERROR(",");
                    context->state = kHAPIPAccessorySerializationState_CharacteristicUnit_Name;
                } else {
                    context->state = kHAPIPAccessorySerializationState_CharacteristicUnit_ValueSeparator;
                }
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicUnit_Name: {
                APPEND_STRING_OR_RETURN_ERROR("\"unit\"");
                context->state = kHAPIPAccessorySerializationState_CharacteristicUnit_NameSeparator;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicUnit_NameSeparator: {
                APPEND_STRING_OR_RETURN_ERROR(":");
                context->state = kHAPIPAccessorySerializationState_CharacteristicUnit_Value;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicUnit_Value: {
                const HAPBaseCharacteristic* baseCharacteristic = GET_CURRENT_CHARACTERISTIC();
                HAPAssert(baseCharacteristic);
                switch (HAPCharacteristicGetUnit(baseCharacteristic)) {
                    case kHAPCharacteristicUnits_None: {
                    }
                        HAPFatalError();
                    case kHAPCharacteristicUnits_Celsius: {
                        APPEND_STRING_OR_RETURN_ERROR("\"celsius\"");
                    } break;
                    case kHAPCharacteristicUnits_ArcDegrees: {
                        APPEND_STRING_OR_RETURN_ERROR("\"arcdegrees\"");
                    } break;
                    case kHAPCharacteristicUnits_Percentage: {
                        APPEND_STRING_OR_RETURN_ERROR("\"percentage\"");
                    } break;
                    case kHAPCharacteristicUnits_Lux: {
                        APPEND_STRING_OR_RETURN_ERROR("\"lux\"");
                    } break;
                    case kHAPCharacteristicUnits_Seconds: {
                        APPEND_STRING_OR_RETURN_ERROR("\"seconds\"");
                    } break;
                }
                context->state = kHAPIPAccessorySerializationState_CharacteristicUnit_ValueSeparator;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicUnit_ValueSeparator: {
                const HAPBaseCharacteristic* baseCharacteristic = GET_CURRENT_CHARACTERISTIC();
                HAPAssert(baseCharacteristic);
                switch (baseCharacteristic->format) {
                    case kHAPCharacteristicFormat_Bool: {
                        context->state = kHAPIPAccessorySerializationState_CharacteristicObject_End;
                    } break;
                    case kHAPCharacteristicFormat_UInt8: {
                        const HAPUInt8Characteristic* uint8Characteristic =
                                (const HAPUInt8Characteristic*) baseCharacteristic;
                        uint8_t minimumValue = uint8Characteristic->constraints.minimumValue;
                        uint8_t maximumValue = uint8Characteristic->constraints.maximumValue;
                        uint8_t stepValue = uint8Characteristic->constraints.stepValue;
                        HAPAssert(minimumValue <= maximumValue);
                        if (minimumValue || maximumValue != UINT8_MAX || stepValue > 1) {
                            APPEND_STRING_OR_RETURN_ERROR(",");
                            context->state = kHAPIPAccessorySerializationState_CharacteristicMinimumValue_Name;
                        } else {
                            context->state = kHAPIPAccessorySerializationState_CharacteristicStepValue_ValueSeparator;
                        }
                    } break;
                    case kHAPCharacteristicFormat_UInt16: {
                        const HAPUInt16Characteristic* uint16Characteristic =
                                (const HAPUInt16Characteristic*) baseCharacteristic;
                        uint16_t minimumValue = uint16Characteristic->constraints.minimumValue;
                        uint16_t maximumValue = uint16Characteristic->constraints.maximumValue;
                        uint16_t stepValue = uint16Characteristic->constraints.stepValue;
                        HAPAssert(minimumValue <= maximumValue);
                        if (minimumValue || maximumValue != UINT16_MAX || stepValue > 1) {
                            APPEND_STRING_OR_RETURN_ERROR(",");
                            context->state = kHAPIPAccessorySerializationState_CharacteristicMinimumValue_Name;
                        } else {
                            context->state = kHAPIPAccessorySerializationState_CharacteristicObject_End;
                        }
                    } break;
                    case kHAPCharacteristicFormat_UInt32: {
                        const HAPUInt32Characteristic* uint32Characteristic =
                                (const HAPUInt32Characteristic*) baseCharacteristic;
                        uint32_t minimumValue = uint32Characteristic->constraints.minimumValue;
                        uint32_t maximumValue = uint32Characteristic->constraints.maximumValue;
                        uint32_t stepValue = uint32Characteristic->constraints.stepValue;
                        HAPAssert(minimumValue <= maximumValue);
                        if (minimumValue || maximumValue != UINT32_MAX || stepValue > 1) {
                            APPEND_STRING_OR_RETURN_ERROR(",");
                            context->state = kHAPIPAccessorySerializationState_CharacteristicMinimumValue_Name;
                        } else {
                            context->state = kHAPIPAccessorySerializationState_CharacteristicObject_End;
                        }
                    } break;
                    case kHAPCharacteristicFormat_UInt64: {
                        const HAPUInt64Characteristic* uint64Characteristic =
                                (const HAPUInt64Characteristic*) baseCharacteristic;
                        uint64_t minimumValue = uint64Characteristic->constraints.minimumValue;
                        uint64_t maximumValue = uint64Characteristic->constraints.maximumValue;
                        uint64_t stepValue = uint64Characteristic->constraints.stepValue;
                        HAPAssert(minimumValue <= maximumValue);
                        if (minimumValue || maximumValue != UINT64_MAX || stepValue > 1) {
                            APPEND_STRING_OR_RETURN_ERROR(",");
                            context->state = kHAPIPAccessorySerializationState_CharacteristicMinimumValue_Name;
                        } else {
                            context->state = kHAPIPAccessorySerializationState_CharacteristicObject_End;
                        }
                    } break;
                    case kHAPCharacteristicFormat_Int: {
                        const HAPIntCharacteristic* intCharacteristic =
                                (const HAPIntCharacteristic*) baseCharacteristic;
                        int32_t minimumValue = intCharacteristic->constraints.minimumValue;
                        int32_t maximumValue = intCharacteristic->constraints.maximumValue;
                        int32_t stepValue = intCharacteristic->constraints.stepValue;
                        HAPAssert(minimumValue <= maximumValue);
                        HAPAssert(stepValue >= 0);
                        if (minimumValue != INT32_MIN || maximumValue != INT32_MAX || stepValue > 1) {
                            APPEND_STRING_OR_RETURN_ERROR(",");
                            context->state = kHAPIPAccessorySerializationState_CharacteristicMinimumValue_Name;
                        } else {
                            context->state = kHAPIPAccessorySerializationState_CharacteristicObject_End;
                        }
                    } break;
                    case kHAPCharacteristicFormat_Float: {
                        const HAPFloatCharacteristic* floatCharacteristic =
                                (const HAPFloatCharacteristic*) baseCharacteristic;
                        float minimumValue = floatCharacteristic->constraints.minimumValue;
                        float maximumValue = floatCharacteristic->constraints.maximumValue;
                        float stepValue = floatCharacteristic->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) || !HAPFloatIsZero(stepValue)) {
                            APPEND_STRING_OR_RETURN_ERROR(",");
                            context->state = kHAPIPAccessorySerializationState_CharacteristicMinimumValue_Name;
                        } else {
                            context->state = kHAPIPAccessorySerializationState_CharacteristicObject_End;
                        }
                    } break;
                    case kHAPCharacteristicFormat_String: {
                        const HAPStringCharacteristic* stringCharacteristic =
                                (const HAPStringCharacteristic*) baseCharacteristic;
                        if (stringCharacteristic->constraints.maxLength !=
                            kHAPIPAccessorySerialization_DefaultMaxStringBytes) {
                            APPEND_STRING_OR_RETURN_ERROR(",");
                            context->state = kHAPIPAccessorySerializationState_CharacteristicMaxLength_Name;
                        } else {
                            context->state = kHAPIPAccessorySerializationState_CharacteristicObject_End;
                        }
                    } break;
                    case kHAPCharacteristicFormat_TLV8: {
                        context->state = kHAPIPAccessorySerializationState_CharacteristicObject_End;
                    } break;
                    case kHAPCharacteristicFormat_Data: {
                        const HAPDataCharacteristic* dataCharacteristic =
                                (const HAPDataCharacteristic*) baseCharacteristic;
                        if (dataCharacteristic->constraints.maxLength !=
                            kHAPIPAccessorySerialization_DefaultMaxDataBytes) {
                            APPEND_STRING_OR_RETURN_ERROR(",");
                            context->state = kHAPIPAccessorySerializationState_CharacteristicMaxDataLength_Name;
                        } else {
                            context->state = kHAPIPAccessorySerializationState_CharacteristicObject_End;
                        }
                    } break;
                }
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicMinimumValue_Name: {
                APPEND_STRING_OR_RETURN_ERROR("\"minValue\"");
                context->state = kHAPIPAccessorySerializationState_CharacteristicMinimumValue_NameSeparator;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicMinimumValue_NameSeparator: {
                APPEND_STRING_OR_RETURN_ERROR(":");
                context->state = kHAPIPAccessorySerializationState_CharacteristicMinimumValue_Value;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicMinimumValue_Value: {
                const HAPBaseCharacteristic* baseCharacteristic = GET_CURRENT_CHARACTERISTIC();
                HAPAssert(baseCharacteristic);
                switch (baseCharacteristic->format) {
                    case kHAPCharacteristicFormat_UInt8: {
                        const HAPUInt8Characteristic* uint8Characteristic =
                                (const HAPUInt8Characteristic*) baseCharacteristic;
                        APPEND_UINT64_OR_RETURN_ERROR(uint8Characteristic->constraints.minimumValue);
                        context->state = kHAPIPAccessorySerializationState_CharacteristicMinimumValue_ValueSeparator;
                    } break;
                    case kHAPCharacteristicFormat_UInt16: {
                        const HAPUInt16Characteristic* uint16Characteristic =
                                (const HAPUInt16Characteristic*) baseCharacteristic;
                        APPEND_UINT64_OR_RETURN_ERROR(uint16Characteristic->constraints.minimumValue);
                        context->state = kHAPIPAccessorySerializationState_CharacteristicMinimumValue_ValueSeparator;
                    } break;
                    case kHAPCharacteristicFormat_UInt32: {
                        const HAPUInt32Characteristic* uint32Characteristic =
                                (const HAPUInt32Characteristic*) baseCharacteristic;
                        APPEND_UINT64_OR_RETURN_ERROR(uint32Characteristic->constraints.minimumValue);
                        context->state = kHAPIPAccessorySerializationState_CharacteristicMinimumValue_ValueSeparator;
                    } break;
                    case kHAPCharacteristicFormat_UInt64: {
                        const HAPUInt64Characteristic* uint64Characteristic =
                                (const HAPUInt64Characteristic*) baseCharacteristic;
                        APPEND_UINT64_OR_RETURN_ERROR(uint64Characteristic->constraints.minimumValue);
                        context->state = kHAPIPAccessorySerializationState_CharacteristicMinimumValue_ValueSeparator;
                    } break;
                    case kHAPCharacteristicFormat_Int: {
                        const HAPIntCharacteristic* intCharacteristic =
                                (const HAPIntCharacteristic*) baseCharacteristic;
                        APPEND_INT32_OR_RETURN_ERROR(intCharacteristic->constraints.minimumValue);
                        context->state = kHAPIPAccessorySerializationState_CharacteristicMinimumValue_ValueSeparator;
                    } break;
                    case kHAPCharacteristicFormat_Float: {
                        const HAPFloatCharacteristic* floatCharacteristic =
                                (const HAPFloatCharacteristic*) baseCharacteristic;
                        APPEND_FLOAT_OR_RETURN_ERROR(floatCharacteristic->constraints.minimumValue);
                        context->state = kHAPIPAccessorySerializationState_CharacteristicMinimumValue_ValueSeparator;
                    } break;
                    case kHAPCharacteristicFormat_Bool:
                    case kHAPCharacteristicFormat_String:
                    case kHAPCharacteristicFormat_TLV8:
                    case kHAPCharacteristicFormat_Data: {
                    }
                        HAPFatalError();
                }
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicMinimumValue_ValueSeparator: {
                APPEND_STRING_OR_RETURN_ERROR(",");
                context->state = kHAPIPAccessorySerializationState_CharacteristicMaximumValue_Name;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicMaximumValue_Name: {
                APPEND_STRING_OR_RETURN_ERROR("\"maxValue\"");
                context->state = kHAPIPAccessorySerializationState_CharacteristicMaximumValue_NameSeparator;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicMaximumValue_NameSeparator: {
                APPEND_STRING_OR_RETURN_ERROR(":");
                context->state = kHAPIPAccessorySerializationState_CharacteristicMaximumValue_Value;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicMaximumValue_Value: {
                const HAPBaseCharacteristic* baseCharacteristic = GET_CURRENT_CHARACTERISTIC();
                HAPAssert(baseCharacteristic);
                switch (baseCharacteristic->format) {
                    case kHAPCharacteristicFormat_UInt8: {
                        const HAPUInt8Characteristic* uint8Characteristic =
                                (const HAPUInt8Characteristic*) baseCharacteristic;
                        APPEND_UINT64_OR_RETURN_ERROR(uint8Characteristic->constraints.maximumValue);
                        context->state = kHAPIPAccessorySerializationState_CharacteristicMaximumValue_ValueSeparator;
                    } break;
                    case kHAPCharacteristicFormat_UInt16: {
                        const HAPUInt16Characteristic* uint16Characteristic =
                                (const HAPUInt16Characteristic*) baseCharacteristic;
                        APPEND_UINT64_OR_RETURN_ERROR(uint16Characteristic->constraints.maximumValue);
                        context->state = kHAPIPAccessorySerializationState_CharacteristicMaximumValue_ValueSeparator;
                    } break;
                    case kHAPCharacteristicFormat_UInt32: {
                        const HAPUInt32Characteristic* uint32Characteristic =
                                (const HAPUInt32Characteristic*) baseCharacteristic;
                        APPEND_UINT64_OR_RETURN_ERROR(uint32Characteristic->constraints.maximumValue);
                        context->state = kHAPIPAccessorySerializationState_CharacteristicMaximumValue_ValueSeparator;
                    } break;
                    case kHAPCharacteristicFormat_UInt64: {
                        const HAPUInt64Characteristic* uint64Characteristic =
                                (const HAPUInt64Characteristic*) baseCharacteristic;
                        APPEND_UINT64_OR_RETURN_ERROR(uint64Characteristic->constraints.maximumValue);
                        context->state = kHAPIPAccessorySerializationState_CharacteristicMaximumValue_ValueSeparator;
                    } break;
                    case kHAPCharacteristicFormat_Int: {
                        const HAPIntCharacteristic* intCharacteristic =
                                (const HAPIntCharacteristic*) baseCharacteristic;
                        APPEND_INT32_OR_RETURN_ERROR(intCharacteristic->constraints.maximumValue);
                        context->state = kHAPIPAccessorySerializationState_CharacteristicMaximumValue_ValueSeparator;
                    } break;
                    case kHAPCharacteristicFormat_Float: {
                        const HAPFloatCharacteristic* floatCharacteristic =
                                (const HAPFloatCharacteristic*) baseCharacteristic;
                        APPEND_FLOAT_OR_RETURN_ERROR(floatCharacteristic->constraints.maximumValue);
                        context->state = kHAPIPAccessorySerializationState_CharacteristicMaximumValue_ValueSeparator;
                    } break;
                    case kHAPCharacteristicFormat_Bool:
                    case kHAPCharacteristicFormat_String:
                    case kHAPCharacteristicFormat_TLV8:
                    case kHAPCharacteristicFormat_Data: {
                    }
                        HAPFatalError();
                }
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicMaximumValue_ValueSeparator: {
                APPEND_STRING_OR_RETURN_ERROR(",");
                context->state = kHAPIPAccessorySerializationState_CharacteristicStepValue_Name;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicStepValue_Name: {
                APPEND_STRING_OR_RETURN_ERROR("\"minStep\"");
                context->state = kHAPIPAccessorySerializationState_CharacteristicStepValue_NameSeparator;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicStepValue_NameSeparator: {
                APPEND_STRING_OR_RETURN_ERROR(":");
                context->state = kHAPIPAccessorySerializationState_CharacteristicStepValue_Value;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicStepValue_Value: {
                const HAPBaseCharacteristic* baseCharacteristic = GET_CURRENT_CHARACTERISTIC();
                HAPAssert(baseCharacteristic);
                switch (baseCharacteristic->format) {
                    case kHAPCharacteristicFormat_UInt8: {
                        const HAPUInt8Characteristic* uint8Characteristic =
                                (const HAPUInt8Characteristic*) baseCharacteristic;
                        APPEND_UINT64_OR_RETURN_ERROR(uint8Characteristic->constraints.stepValue);
                        context->state = kHAPIPAccessorySerializationState_CharacteristicStepValue_ValueSeparator;
                    } break;
                    case kHAPCharacteristicFormat_UInt16: {
                        const HAPUInt16Characteristic* uint16Characteristic =
                                (const HAPUInt16Characteristic*) baseCharacteristic;
                        APPEND_UINT64_OR_RETURN_ERROR(uint16Characteristic->constraints.stepValue);
                        context->state = kHAPIPAccessorySerializationState_CharacteristicObject_End;
                    } break;
                    case kHAPCharacteristicFormat_UInt32: {
                        const HAPUInt32Characteristic* uint32Characteristic =
                                (const HAPUInt32Characteristic*) baseCharacteristic;
                        APPEND_UINT64_OR_RETURN_ERROR(uint32Characteristic->constraints.stepValue);
                        context->state = kHAPIPAccessorySerializationState_CharacteristicObject_End;
                    } break;
                    case kHAPCharacteristicFormat_UInt64: {
                        const HAPUInt64Characteristic* uint64Characteristic =
                                (const HAPUInt64Characteristic*) baseCharacteristic;
                        APPEND_UINT64_OR_RETURN_ERROR(uint64Characteristic->constraints.stepValue);
                        context->state = kHAPIPAccessorySerializationState_CharacteristicObject_End;
                    } break;
                    case kHAPCharacteristicFormat_Int: {
                        const HAPIntCharacteristic* intCharacteristic =
                                (const HAPIntCharacteristic*) baseCharacteristic;
                        APPEND_INT32_OR_RETURN_ERROR(intCharacteristic->constraints.stepValue);
                        context->state = kHAPIPAccessorySerializationState_CharacteristicObject_End;
                    } break;
                    case kHAPCharacteristicFormat_Float: {
                        const HAPFloatCharacteristic* floatCharacteristic =
                                (const HAPFloatCharacteristic*) baseCharacteristic;
                        APPEND_FLOAT_OR_RETURN_ERROR(floatCharacteristic->constraints.stepValue);
                        context->state = kHAPIPAccessorySerializationState_CharacteristicObject_End;
                    } break;
                    case kHAPCharacteristicFormat_Bool:
                    case kHAPCharacteristicFormat_String:
                    case kHAPCharacteristicFormat_TLV8:
                    case kHAPCharacteristicFormat_Data: {
                    }
                        HAPFatalError();
                }
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicStepValue_ValueSeparator: {
                const HAPBaseCharacteristic* baseCharacteristic = GET_CURRENT_CHARACTERISTIC();
                HAPAssert(baseCharacteristic);
                HAPAssert(baseCharacteristic->format == kHAPCharacteristicFormat_UInt8);
                const HAPUInt8Characteristic* uint8Characteristic = (const HAPUInt8Characteristic*) baseCharacteristic;
                if (HAPUUIDIsAppleDefined(uint8Characteristic->characteristicType)) {
                    if (uint8Characteristic->constraints.validValues) {
                        APPEND_STRING_OR_RETURN_ERROR(",");
                        context->state = kHAPIPAccessorySerializationState_CharacteristicValidValuesArray_Name;
                    } else if (uint8Characteristic->constraints.validValuesRanges) {
                        APPEND_STRING_OR_RETURN_ERROR(",");
                        context->state = kHAPIPAccessorySerializationState_CharacteristicValidValuesRangesArray_Name;
                    } else {
                        context->state = kHAPIPAccessorySerializationState_CharacteristicObject_End;
                    }
                } else {
                    context->state = kHAPIPAccessorySerializationState_CharacteristicObject_End;
                }
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicMaxLength_Name: {
                APPEND_STRING_OR_RETURN_ERROR("\"maxLen\"");
                context->state = kHAPIPAccessorySerializationState_CharacteristicMaxLength_NameSeparator;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicMaxLength_NameSeparator: {
                APPEND_STRING_OR_RETURN_ERROR(":");
                context->state = kHAPIPAccessorySerializationState_CharacteristicMaxLength_Value;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicMaxLength_Value: {
                const HAPBaseCharacteristic* baseCharacteristic = GET_CURRENT_CHARACTERISTIC();
                HAPAssert(baseCharacteristic);
                HAPAssert(baseCharacteristic->format == kHAPCharacteristicFormat_String);
                const HAPStringCharacteristic* stringCharacteristic =
                        (const HAPStringCharacteristic*) baseCharacteristic;
                APPEND_UINT64_OR_RETURN_ERROR(stringCharacteristic->constraints.maxLength);
                context->state = kHAPIPAccessorySerializationState_CharacteristicObject_End;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicMaxDataLength_Name: {
                APPEND_STRING_OR_RETURN_ERROR("\"maxDataLen\"");
                context->state = kHAPIPAccessorySerializationState_CharacteristicMaxDataLength_NameSeparator;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicMaxDataLength_NameSeparator: {
                APPEND_STRING_OR_RETURN_ERROR(":");
                context->state = kHAPIPAccessorySerializationState_CharacteristicMaxDataLength_Value;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicMaxDataLength_Value: {
                const HAPBaseCharacteristic* baseCharacteristic = GET_CURRENT_CHARACTERISTIC();
                HAPAssert(baseCharacteristic);
                HAPAssert(baseCharacteristic->format == kHAPCharacteristicFormat_Data);
                const HAPDataCharacteristic* dataCharacteristic = (const HAPDataCharacteristic*) baseCharacteristic;
                APPEND_UINT64_OR_RETURN_ERROR(dataCharacteristic->constraints.maxLength);
                context->state = kHAPIPAccessorySerializationState_CharacteristicObject_End;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicValidValuesArray_Name: {
                APPEND_STRING_OR_RETURN_ERROR("\"valid-values\"");
                context->state = kHAPIPAccessorySerializationState_CharacteristicValidValuesArray_NameSeparator;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicValidValuesArray_NameSeparator: {
                APPEND_STRING_OR_RETURN_ERROR(":");
                context->state = kHAPIPAccessorySerializationState_CharacteristicValidValuesArray_Begin;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicValidValuesArray_Begin: {
                APPEND_STRING_OR_RETURN_ERROR("[");
                const HAPBaseCharacteristic* baseCharacteristic = GET_CURRENT_CHARACTERISTIC();
                HAPAssert(baseCharacteristic);
                HAPAssert(baseCharacteristic->format == kHAPCharacteristicFormat_UInt8);
                const HAPUInt8Characteristic* uint8Characteristic = (const HAPUInt8Characteristic*) baseCharacteristic;
                HAPAssert(HAPUUIDIsAppleDefined(uint8Characteristic->characteristicType));
                const uint8_t* const* validValues = uint8Characteristic->constraints.validValues;
                HAPAssert(validValues);
                context->index = 0;
                if (validValues[context->index]) {
                    context->state = kHAPIPAccessorySerializationState_CharacteristicValidValue_Value;
                } else {
                    context->state = kHAPIPAccessorySerializationState_CharacteristicValidValuesArray_End;
                }
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicValidValuesArray_End: {
                APPEND_STRING_OR_RETURN_ERROR("]");
                context->state = kHAPIPAccessorySerializationState_CharacteristicValidValuesArray_ValueSeparator;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicValidValuesArray_ValueSeparator: {
                const HAPBaseCharacteristic* baseCharacteristic = GET_CURRENT_CHARACTERISTIC();
                HAPAssert(baseCharacteristic);
                HAPAssert(baseCharacteristic->format == kHAPCharacteristicFormat_UInt8);
                const HAPUInt8Characteristic* uint8Characteristic = (const HAPUInt8Characteristic*) baseCharacteristic;
                HAPAssert(HAPUUIDIsAppleDefined(uint8Characteristic->characteristicType));
                if (uint8Characteristic->constraints.validValuesRanges) {
                    APPEND_STRING_OR_RETURN_ERROR(",");
                    context->state = kHAPIPAccessorySerializationState_CharacteristicValidValuesRangesArray_Name;
                } else {
                    context->state = kHAPIPAccessorySerializationState_CharacteristicObject_End;
                }
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicValidValue_Value: {
                const HAPBaseCharacteristic* baseCharacteristic = GET_CURRENT_CHARACTERISTIC();
                HAPAssert(baseCharacteristic);
                HAPAssert(baseCharacteristic->format == kHAPCharacteristicFormat_UInt8);
                const HAPUInt8Characteristic* uint8Characteristic = (const HAPUInt8Characteristic*) baseCharacteristic;
                HAPAssert(HAPUUIDIsAppleDefined(uint8Characteristic->characteristicType));
                const uint8_t* const* validValues = uint8Characteristic->constraints.validValues;
                HAPAssert(validValues);
                HAPAssert(validValues[context->index]);
                APPEND_UINT64_OR_RETURN_ERROR(*validValues[context->index]);
                HAPAssert(context->index < UINT8_MAX);
                context->index++;
                if (validValues[context->index]) {
                    context->state = kHAPIPAccessorySerializationState_CharacteristicValidValue_Separator;
                } else {
                    context->state = kHAPIPAccessorySerializationState_CharacteristicValidValuesArray_End;
                }
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicValidValue_Separator: {
                APPEND_STRING_OR_RETURN_ERROR(",");
                context->state = kHAPIPAccessorySerializationState_CharacteristicValidValue_Value;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicValidValuesRangesArray_Name: {
                APPEND_STRING_OR_RETURN_ERROR("\"valid-values-range\"");
                context->state = kHAPIPAccessorySerializationState_CharacteristicValidValuesRangesArray_NameSeparator;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicValidValuesRangesArray_NameSeparator: {
                APPEND_STRING_OR_RETURN_ERROR(":");
                context->state = kHAPIPAccessorySerializationState_CharacteristicValidValuesRangesArray_Begin;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicValidValuesRangesArray_Begin: {
                APPEND_STRING_OR_RETURN_ERROR("[");
                const HAPBaseCharacteristic* baseCharacteristic = GET_CURRENT_CHARACTERISTIC();
                HAPAssert(baseCharacteristic);
                HAPAssert(baseCharacteristic->format == kHAPCharacteristicFormat_UInt8);
                const HAPUInt8Characteristic* uint8Characteristic = (const HAPUInt8Characteristic*) baseCharacteristic;
                HAPAssert(HAPUUIDIsAppleDefined(uint8Characteristic->characteristicType));
                const HAPUInt8CharacteristicValidValuesRange* const* validValuesRanges =
                        uint8Characteristic->constraints.validValuesRanges;
                HAPAssert(validValuesRanges);
                context->index = 0;
                if (validValuesRanges[context->index]) {
                    context->state = kHAPIPAccessorySerializationState_CharacteristicValidValuesRangeArray_Begin;
                } else {
                    context->state = kHAPIPAccessorySerializationState_CharacteristicValidValuesRangesArray_End;
                }
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicValidValuesRangesArray_End: {
                APPEND_STRING_OR_RETURN_ERROR("]");
                context->state = kHAPIPAccessorySerializationState_CharacteristicObject_End;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicValidValuesRangeArray_Begin: {
                APPEND_STRING_OR_RETURN_ERROR("[");
                context->state = kHAPIPAccessorySerializationState_CharacteristicValidValuesRangeStart_Value;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicValidValuesRangeArray_End: {
                APPEND_STRING_OR_RETURN_ERROR("]");
                const HAPBaseCharacteristic* baseCharacteristic = GET_CURRENT_CHARACTERISTIC();
                HAPAssert(baseCharacteristic);
                HAPAssert(baseCharacteristic->format == kHAPCharacteristicFormat_UInt8);
                const HAPUInt8Characteristic* uint8Characteristic = (const HAPUInt8Characteristic*) baseCharacteristic;
                HAPAssert(HAPUUIDIsAppleDefined(uint8Characteristic->characteristicType));
                const HAPUInt8CharacteristicValidValuesRange* const* validValuesRanges =
                        uint8Characteristic->constraints.validValuesRanges;
                HAPAssert(validValuesRanges);
                HAPAssert(context->index < UINT8_MAX);
                context->index++;
                if (validValuesRanges[context->index]) {
                    context->state = kHAPIPAccessorySerializationState_CharacteristicValidValuesRangeArray_Separator;
                } else {
                    context->state = kHAPIPAccessorySerializationState_CharacteristicValidValuesRangesArray_End;
                }
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicValidValuesRangeArray_Separator: {
                APPEND_STRING_OR_RETURN_ERROR(",");
                context->state = kHAPIPAccessorySerializationState_CharacteristicValidValuesRangeArray_Begin;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicValidValuesRangeStart_Value: {
                const HAPBaseCharacteristic* baseCharacteristic = GET_CURRENT_CHARACTERISTIC();
                HAPAssert(baseCharacteristic);
                HAPAssert(baseCharacteristic->format == kHAPCharacteristicFormat_UInt8);
                const HAPUInt8Characteristic* uint8Characteristic = (const HAPUInt8Characteristic*) baseCharacteristic;
                HAPAssert(HAPUUIDIsAppleDefined(uint8Characteristic->characteristicType));
                const HAPUInt8CharacteristicValidValuesRange* const* validValuesRanges =
                        uint8Characteristic->constraints.validValuesRanges;
                HAPAssert(validValuesRanges);
                HAPAssert(validValuesRanges[context->index]);
                APPEND_UINT64_OR_RETURN_ERROR(validValuesRanges[context->index]->start);
                context->state = kHAPIPAccessorySerializationState_CharacteristicValidValuesRange_Separator;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicValidValuesRangeEnd_Value: {
                const HAPBaseCharacteristic* baseCharacteristic = GET_CURRENT_CHARACTERISTIC();
                HAPAssert(baseCharacteristic);
                HAPAssert(baseCharacteristic->format == kHAPCharacteristicFormat_UInt8);
                const HAPUInt8Characteristic* uint8Characteristic = (const HAPUInt8Characteristic*) baseCharacteristic;
                HAPAssert(HAPUUIDIsAppleDefined(uint8Characteristic->characteristicType));
                const HAPUInt8CharacteristicValidValuesRange* const* validValuesRanges =
                        uint8Characteristic->constraints.validValuesRanges;
                HAPAssert(validValuesRanges);
                HAPAssert(validValuesRanges[context->index]);
                APPEND_UINT64_OR_RETURN_ERROR(validValuesRanges[context->index]->end);
                context->state = kHAPIPAccessorySerializationState_CharacteristicValidValuesRangeArray_End;
            }
                continue;
            case kHAPIPAccessorySerializationState_CharacteristicValidValuesRange_Separator: {
                APPEND_STRING_OR_RETURN_ERROR(",");
                context->state = kHAPIPAccessorySerializationState_CharacteristicValidValuesRangeEnd_Value;
            }
                continue;
            case kHAPIPAccessorySerializationState_ResponseIsComplete: {
            }
                HAPFatalError();
        }
        HAPFatalError();
    } while ((*numBytes < minBytes) && (context->state != kHAPIPAccessorySerializationState_ResponseIsComplete));

#undef APPEND_FLOAT_OR_RETURN_ERROR
#undef APPEND_INT32_OR_RETURN_ERROR
#undef APPEND_UINT64_OR_RETURN_ERROR
#undef APPEND_UUID_OR_RETURN_ERROR
#undef APPEND_STRING_OR_RETURN_ERROR

#undef GET_CURRENT_CHARACTERISTIC
#undef GET_CURRENT_SERVICE
#undef GET_CURRENT_ACCESSORY

    return kHAPError_None;
}